cs.fstags


Namecs.fstags JSON
Version 20240422 PyPI version JSON
download
home_pageNone
SummarySimple filesystem based file tagging and the associated `fstags` command line script.
upload_time2024-04-22 03:03:42
maintainerNone
docs_urlNone
authorNone
requires_pythonNone
licenseGNU General Public License v3 or later (GPLv3+)
keywords python3
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            Simple filesystem based file tagging
and the associated `fstags` command line script.

*Latest release 20240422*:
* Bugfix: .fstags files should no longer get Python repr() output for weirdly typed tag values.
* Some other minor internal updates.

Many basic tasks can be performed with the `fstags` command line utility,
documented under the `FSTagsCommand` class below.

Why `fstags`?
By storing the tags in a separate file we:
* can store tags without modifying a file
* do not need to know the file's format,
  or even whether that format supports metadata
* can process tags on any kind of file
* because tags are inherited from parent directories,
  tags can be automatically acquired merely by arranging your file tree

Tags are stored in the file `.fstags` in each directory;
there is a line for each entry in the directory
consisting of the directory entry name and the associated tags.

Tags may be "bare", or have a value.
If there is a value it is expressed with an equals (`'='`)
followed by the JSON encoding of the value.

The tags for a file are the union of its direct tags
and all relevant ancestor tags,
with priority given to tags closer to the file.

For example, a media file for a television episode with the pathname
`/path/to/series-name/season-02/episode-name--s02e03--something.mp4`
might have the tags:

    series_title="Series Full Name"
    season=2
    sf
    episode=3
    episode_title="Full Episode Title"

obtained from the following `.fstags` entries:
* tag file `/path/to/.fstags`:

    series-name sf series_title="Series Full Name"

* tag file `/path/to/series-name/.fstags`:

  season-02 season=2

* tag file `/path/to/series-name/season-02/.fstags`:

  episode-name--s02e03--something.mp4 episode=3 episode_title="Full Episode Title"

## `fstags` Examples ##

### Backing up a media tree too big for the removable drives ###

Walk the media tree for files tagged for backup to `archive2`:

    fstags find /path/to/media backup=archive2

Walk the media tree for files not assigned to a backup archive:

    fstags find /path/to/media -backup

Backup the `archive2` files using `rsync`:

    fstags find --for-rsync /path/to/media backup=archive2 \
    | rsync -ia --include-from=- /path/to/media /path/to/backup_archive2

## Class `CascadeRule`

A cascade rule of possible source tag names to provide a target tag.

*Method `CascadeRule.infer_tag(self, tagset)`*:
Apply the rule to the `TagSet` `tagset`.
Return a new `Tag(self.target,value)`
for the first cascade `value` found in `tagset`,
or `None` if there is no match.

## `DEFAULT_FSTAGS = FSTags('.fstags')`

A class to examine filesystem tags.

## Class `FSTags(cs.resources.MultiOpenMixin)`

A class to examine filesystem tags.

*Method `FSTags.__init__(self, tagsfile_basename=None, ontology_filepath=None, physical=None, update_mapping: Optional[Mapping] = None, update_prefix: Optional[str] = 'cs.fstags', update_uuid_tag_name: Optional[str] = 'uuid')`*:
Initialise the `FSTags` instance.

Parameters:
* `tagsfile_basename`: optional basename forthe backing tags files,
  default from `TAGSFILE_BASENAME`: `'.fstags'`
* `ontology_filepath`: optional filesystem path for an associated ontology
* `physical`: optional flag for the associated `FSTagsConfig`
  specifying whether `TagFile`s are indexed by their physical or logical
  filesystem paths
* `update_mapping`: optional secondary mapping to which to mirror
  tags, such as an `SQLTags`;
  the default comes from an `SQLTags` specified by the
  environment variable `$FSTAGS_UPDATE_MAPPING`
  if present
* `update_prefix`: optional key prefix for use in the secondary mapping;
  the default comes from the environment variable
  `$FSTAGS_UPDATE_MAPPING_PREFIX` if present,
  otherwise `'cs.fstags'`
* `update_uuid_tag_name`: optional name for the per file UUID tag name;
  default `'uuid'`

*Method `FSTags.__getitem__(self, path) -> 'TaggedPath'`*:
Return the `TaggedPath` for `abspath(path)`.

*Method `FSTags.apply_tag_choices(self, tag_choices, paths)`*:
Apply the `tag_choices` to `paths`.

Parameters:
* `tag_choices`:
  an iterable of `Tag` or an equality `TagBasedTest`.
  Each item applies or removes a `Tag`
  from each path's direct tags.
* `paths`:
  an iterable of filesystem paths.

*Method `FSTags.attach_path(self, attach, srcpath, dstpath, *, force=False, crop_ok=False)`*:
Attach `srcpath` to `dstpath` using the `attach` callable.

Parameters:
* `attach`: callable accepting `attach(srcpath,dstpath)`
  to do the desired attachment,
  such as a copy, link or move
* `srcpath`: the source filesystem object
* `dstpath`: the destination filesystem object
* `crop_ok`: if true and the OS raises `OSError(ENAMETOOLONG)`
  attempt to crop the name before the file extension and retry
* `force`: default `False`.
  If true and the destination exists
  try to remove it before calling `attach`.
  Otherwise if the destination exists
  raise a `ValueError`.

*Method `FSTags.cascade_tags(self, tags, cascade_rules=None)`*:
Yield `Tag`s
which cascade from the `TagSet` `tags`
via `cascade_rules` (an iterable of `CascadeRules`).

*Method `FSTags.copy(self, srcpath, dstpath, **kw)`*:
Copy `srcpath` to `dstpath`.

*Method `FSTags.dir_tagfile(self, dirpath: str) -> 'FSTagsTagFile'`*:
Return the `FSTagsTagFile` associated with `dirpath`.

*Method `FSTags.edit_dirpath(self, dirpath, all_names=False)`*:
Edit the filenames and tags in a directory.

If `all_names` is true, include names commencing with a dot,
otherwise exclude them.

*Method `FSTags.export_xattrs(self, paths)`*:
Import the extended attributes of `paths`
and use them to update the fstags.

*Method `FSTags.find(self, path, tag_tests, use_direct_tags=False)`*:
Walk the file tree from `path`
searching for files matching the supplied `tag_tests`.
Yield the matching file paths.

Parameters:
* `path`: the top of the file tree to walk
* `tag_tests`: a sequence of `TagBasedTest`s
* `use_direct_tags`: test the `direct_tags` if true,
  otherwise the `all_tags`.
  Default: `False`

*Method `FSTags.find_ontology(self, dirpath, ontbase=None)`*:
Locate an ontology for the directory `dirpath`.
The optional `ontbase` may override the relative path to the file,
default is `self.ontology_filepath`.
Return a `TagOntology` or `None` if not found.

*Method `FSTags.find_ontpath(self, dirpath, ontbase=None)`*:
Locate an ontology file for the directory `dirpath`.
The optional `ontbase` may override the relative path to the file,
default is `self.ontology_filepath`.
Return the found ontology file or `None` if not found.

*Method `FSTags.import_xattrs(self, paths)`*:
Update the extended attributes of `paths`
from their fstags.

*Method `FSTags.keypath(self, fspath)`*:
Compute the absolute path used to index a `TaggedPath` instance.

This returns `realpath(fspath)` if `self.config.physical`,
otherwise `abspath(fspath)`.

*Method `FSTags.link(self, srcpath, dstpath, **kw)`*:
Link `srcpath` to `dstpath`.

*Method `FSTags.move(self, srcpath, dstpath, **kw)`*:
Move `srcpath` to `dstpath`.

*Method `FSTags.mv(self, srcpath: str, dstpath: str, *, symlink=False, remove=True)`*:
Move (or link or symlink) `srcpath` to `dstpath`.

Parameters:
* `srcpath`: the source filesystem path
* `dstpath`: the destination filesystem path
* `symlink`: default `False`: if true, make a symbolic link
* `remove`: default `True`: if true, remove `srcpath` after
  hard linking to `dstpath`

*Property `FSTags.ontology`*:
The primary `TagsOntology`, or `None` if `self.ontology_filepath` was `None`.

*Property `FSTags.ontology_filepath`*:
The ontology file basename.

*Method `FSTags.ontology_for(self, path, ontbase=None)`*:
Return the `TagsOntology` associated with `path`.
Returns `None` if an ontology cannot be found.

*Method `FSTags.open_ontology(self, ontpath)`*:
Open the contology file at `ontpath`.

*Method `FSTags.path_tagfiles(self, fspath)`*:
Generator yielding a sequence of `(FSTagsTagFile,name)` pairs
where `name` is the key within the `FSTagsTagFile`
for the `FSTagsTagFile`s affecting `fspath`
in order from the root to `dirname(fspath)`.

*Method `FSTags.resolve_format_string(self, format_string)`*:
See if `format_string` looks like `[`*clausename*`]`*entryname*.
if so, return the corresponding config entry string,
otherwise return `format_string` unchanged.

*Method `FSTags.scrub(self, path)`*:
Scrub tags for names which do not exist in the filesystem.

*Method `FSTags.startup_shutdown(self)`*:
Sync tag files and db mapping on final close.

*Method `FSTags.sync(self)`*:
Flush modified tag files.

*Method `FSTags.tagfile_for(self, fspath)`*:
Return the `FSTagsTagFile` storing the `Tag`s for `fspath`.

*Property `FSTags.tagsfile_basename`*:
The tag file basename.

*Method `FSTags.test(self, path, tag_tests, use_direct_tags=False)`*:
Test a path against `tag_tests`.

Parameters:
* `path`: path to test
* `tag_tests`: an iterable of `TagBasedTest`s
* `use_direct_tags`: test the `direct_tags` if true,
  otherwise the `all_tags`.
  Default: `False`

## Class `FSTagsCommand(cs.cmdutils.BaseCommand, cs.tagset.TagsCommandMixin)`

`fstags` main command line utility.

Command line implementation.

Usage summary:

    Usage: fstags [-o ontology] [-P] subcommand [...]
      -o ontology   Specify the path to an ontology file.
      -P            Physical. Resolve pathnames through symlinks.
                    Default ~/.fstagsrc[general]physical or False.
      Subcommands:
        autotag paths...
          Tag paths based on rules from the rc file.
        cp [-finv] srcpath dstpath, cp [-finv] srcpaths... dstdirpath
          POSIX cp(1) equivalent, but also copying tags:
          copy files and their tags into targetdir.
          -f  Force: remove destination if it exists.
          -i  Interactive: fail if the destination exists.
          -n  No remove: fail if the destination exists.
          -v  Verbose: show copied files.
        cptags srcpath dstpath
          Copy the direct tags from srcpath to dstpath.
        edit [-ad] [path]
          Edit the direct tagsets of path, default: '.'
          If path is a directory, provide the tags of its entries.
          Otherwise edit just the tags for path.
          -a    List all names in directory edit mode; normally
                names commencing with a dot are omitted.
          -d    Treat directories like files: edit just its tags.
        export [-a] [--direct] path {tag[=value]|-tag}...
          Export tags for files from paths matching all the constraints.
          -a        Export all paths, not just those with tags.
          --direct  Export the direct tags instead of the computed tags.
          The output is in the same CSV format as that from "sqltags export",
          with the following columns:
          * unixtime: the file's st_mtime from os.stat.
          * id: empty
          * name: the file path
          * tags: the file's direct or indirect tags
        find [--direct] [--for-rsync] [-o output_format] path {tag[=value]|-tag}...
          List files from path matching all the constraints.
          --direct    Use direct tags instead of all tags.
          --for-rsync Instead of listing matching paths, emit a
                      sequence of rsync(1) patterns suitable for use with
                      --include-from in order to do a selective rsync of the
                      matched paths.
          -o output_format
                      Use output_format as a Python format string to lay out
                      the listing.
                      Default: {fspath}
        help [-l] [subcommand-names...]
          Print help for subcommands.
          This outputs the full help for the named subcommands,
          or the short help for all subcommands if no names are specified.
          -l  Long help even if no subcommand-names provided.
        import {-|srcpath}...
          Import CSV data in the format emitted by "export".
          Each argument is a file path or "-", indicating standard input.
        infer pathname
          Print the base and inferred tags for pathname.
        json_import --prefix=tag_prefix {-|path} {-|tags.json}
          Apply JSON data to path.
          A path named "-" indicates that paths should be read from
          the standard input.
          The JSON tag data come from the file "tags.json"; the name
          "-" indicates that the JSON data should be read from the
          standard input.
        ln [-finv] srcpath dstpath, ln [-finv] srcpaths... dstdirpath
          POSIX ln(1) equivalent, but also copying the tags:
          link files and their tags into targetdir.
          -f  Force: remove destination if it exists.
          -i  Interactive: fail if the destination exists.
          -n  No remove: fail if the destination exists.
          -v  Verbose: show linked files.
        ls [-dlr] [--direct] [-o output_format] [paths...]
          List files from paths and their tags.
          -d          Treat directories like files, do not recurse.
          --direct    List direct tags instead of all tags.
          -l          Long format.
          -o output_format
                      Use output_format as a Python format string to lay out
                      the listing.
                      Default: {fspath:json} {tags}
          -r          Recurse into subdirectories.
        mv [-finv] srcpath dstpath, mv [-finv] srcpaths... dstdirpath
          POSIX mv(1) equivalent, but also copying the tags:
          move files and their tags into targetdir.
          -f  Force: remove destination if it exists.
          -i  Interactive: fail if the destination exists.
          -n  No remove: fail if the destination exists.
          -v  Verbose: show moved files.
        ns [-d] [--direct] [paths...]
          Report on the available primary namespace fields for formatting.
          Note that because the namespace used for formatting has
          inferred field names there are also unshown secondary field
          names available in format strings.
          -d          Treat directories like files, do not recurse.
          --direct    List direct tags instead of all tags.
        ont [subcommand [args...]]
          With no arguments, print the ontology.
        rename -o basename_format paths...
          Rename paths according to a format string.
          -o basename_format
              Use basename_format as a Python format string to
              compute the new basename for each path.
        scrub paths...
          Remove all tags for missing paths.
          If a path is a directory, scrub the immediate paths in the directory.
        shell
          Run a command prompt via cmd.Cmd using this command's subcommands.
        tag {-|path} {tag[=value]|-tag}...
          Tag a path with multiple tags.
          With the form "-tag", remove that tag from the direct tags.
          A path named "-" indicates that paths should be read from the
          standard input.
        tagfile tagfile_path [subcommand ...]
          Subcommands:
            tag tagset_name {tag[=value]|-tag}...
              Directly modify tag_name within the tag file tagfile_path.
        tagpaths {tag[=value]|-tag} {-|paths...}
          Tag multiple paths.
          With the form "-tag", remove the tag from the immediate tags.
          A single path named "-" indicates that paths should be read
          from the standard input.
        test [--direct] path {tag[=value]|-tag}...
          Test whether the path matches all the constraints.
          --direct    Use direct tags instead of all tags.
        xattr_export {-|paths...}
          Import tag information from extended attributes.
        xattr_import {-|paths...}
          Update extended attributes from tags.

*`FSTagsCommand.Options`*

*Method `FSTagsCommand.apply_opt(self, opt, val)`*:
Apply command line option.

*Method `FSTagsCommand.cmd_autotag(self, argv, *, upd: cs.upd.Upd, runstate: Optional[cs.resources.RunState] = <function <lambda> at 0x10b33df30>)`*:
Usage: {cmd} paths...
Tag paths based on rules from the rc file.

*Method `FSTagsCommand.cmd_cp(self, argv)`*:
Usage: {cmd} [-finv] srcpath dstpath, {cmd} [-finv] srcpaths... dstdirpath
POSIX cp(1) equivalent, but also copying tags:
copy files and their tags into targetdir.
-f  Force: remove destination if it exists.
-i  Interactive: fail if the destination exists.
-n  No remove: fail if the destination exists.
-v  Verbose: show copied files.

*Method `FSTagsCommand.cmd_cptags(self, argv)`*:
Usage: {cmd} srcpath dstpath
Copy the direct tags from srcpath to dstpath.

*Method `FSTagsCommand.cmd_edit(self, argv)`*:
Usage: {cmd} [-ad] [path]
Edit the direct tagsets of path, default: '.'
If path is a directory, provide the tags of its entries.
Otherwise edit just the tags for path.
-a    List all names in directory edit mode; normally
      names commencing with a dot are omitted.
-d    Treat directories like files: edit just its tags.

*Method `FSTagsCommand.cmd_export(self, argv, *, runstate: Optional[cs.resources.RunState] = <function <lambda> at 0x10b33df30>)`*:
Usage: {cmd} [-a] [--direct] path {{tag[=value]|-tag}}...
Export tags for files from paths matching all the constraints.
-a        Export all paths, not just those with tags.
--direct  Export the direct tags instead of the computed tags.
The output is in the same CSV format as that from "sqltags export",
with the following columns:
* unixtime: the file's st_mtime from os.stat.
* id: empty
* name: the file path
* tags: the file's direct or indirect tags

*Method `FSTagsCommand.cmd_find(self, argv, *, runstate: Optional[cs.resources.RunState] = <function <lambda> at 0x10b33df30>)`*:
Usage: {cmd} [--direct] [--for-rsync] [-o output_format] path {{tag[=value]|-tag}}...
List files from path matching all the constraints.
--direct    Use direct tags instead of all tags.
--for-rsync Instead of listing matching paths, emit a
            sequence of rsync(1) patterns suitable for use with
            --include-from in order to do a selective rsync of the
            matched paths.
-o output_format
            Use output_format as a Python format string to lay out
            the listing.
            Default: {FIND_OUTPUT_FORMAT_DEFAULT}

*Method `FSTagsCommand.cmd_import(self, argv)`*:
Usage: {cmd} {{-|srcpath}}...
Import CSV data in the format emitted by "export".
Each argument is a file path or "-", indicating standard input.

*Method `FSTagsCommand.cmd_infer(self, argv)`*:
Usage: {cmd} pathname
Print the base and inferred tags for pathname.

*Method `FSTagsCommand.cmd_json_import(self, argv)`*:
Usage: json_import --prefix=tag_prefix {{-|path}} {{-|tags.json}}
Apply JSON data to path.
A path named "-" indicates that paths should be read from
the standard input.
The JSON tag data come from the file "tags.json"; the name
"-" indicates that the JSON data should be read from the
standard input.

*Method `FSTagsCommand.cmd_ln(self, argv)`*:
Usage: {cmd} [-finv] srcpath dstpath, {cmd} [-finv] srcpaths... dstdirpath
POSIX ln(1) equivalent, but also copying the tags:
link files and their tags into targetdir.
-f  Force: remove destination if it exists.
-i  Interactive: fail if the destination exists.
-n  No remove: fail if the destination exists.
-v  Verbose: show linked files.

*Method `FSTagsCommand.cmd_ls(self, argv, *, runstate: Optional[cs.resources.RunState] = <function <lambda> at 0x10b33df30>)`*:
Usage: {cmd} [-dlr] [--direct] [-o output_format] [paths...]
List files from paths and their tags.
-d          Treat directories like files, do not recurse.
--direct    List direct tags instead of all tags.
-l          Long format.
-o output_format
            Use output_format as a Python format string to lay out
            the listing.
            Default: {LS_OUTPUT_FORMAT_DEFAULT}
-r          Recurse into subdirectories.

*Method `FSTagsCommand.cmd_mv(self, argv)`*:
Usage: {cmd} [-finv] srcpath dstpath, {cmd} [-finv] srcpaths... dstdirpath
POSIX mv(1) equivalent, but also copying the tags:
move files and their tags into targetdir.
-f  Force: remove destination if it exists.
-i  Interactive: fail if the destination exists.
-n  No remove: fail if the destination exists.
-v  Verbose: show moved files.

*Method `FSTagsCommand.cmd_ns(self, argv)`*:
Usage: {cmd} [-d] [--direct] [paths...]
Report on the available primary namespace fields for formatting.
Note that because the namespace used for formatting has
inferred field names there are also unshown secondary field
names available in format strings.
-d          Treat directories like files, do not recurse.
--direct    List direct tags instead of all tags.

*Method `FSTagsCommand.cmd_ont(self, argv)`*:
Ontology operations.

Usage: {cmd} [subcommand [args...]]
  With no arguments, print the ontology.

*Method `FSTagsCommand.cmd_rename(self, argv)`*:
Usage: {cmd} -o basename_format paths...
Rename paths according to a format string.
-o basename_format
    Use basename_format as a Python format string to
    compute the new basename for each path.

*Method `FSTagsCommand.cmd_scrub(self, argv)`*:
Usage: {cmd} paths...
Remove all tags for missing paths.
If a path is a directory, scrub the immediate paths in the directory.

*Method `FSTagsCommand.cmd_tag(self, argv)`*:
Usage: {cmd} {{-|path}} {{tag[=value]|-tag}}...
Tag a path with multiple tags.
With the form "-tag", remove that tag from the direct tags.
A path named "-" indicates that paths should be read from the
standard input.

*Method `FSTagsCommand.cmd_tagfile(self, argv)`*:
Usage: {cmd} tagfile_path [subcommand ...]
Subcommands:
  tag tagset_name {{tag[=value]|-tag}}...
    Directly modify tag_name within the tag file tagfile_path.

*Method `FSTagsCommand.cmd_tagpaths(self, argv)`*:
Usage: {cmd} {{tag[=value]|-tag}} {{-|paths...}}
Tag multiple paths.
With the form "-tag", remove the tag from the immediate tags.
A single path named "-" indicates that paths should be read
from the standard input.

*Method `FSTagsCommand.cmd_test(self, argv)`*:
Usage: {cmd} [--direct] path {{tag[=value]|-tag}}...
Test whether the path matches all the constraints.
--direct    Use direct tags instead of all tags.

*Method `FSTagsCommand.cmd_xattr_export(self, argv)`*:
Usage: {cmd} {{-|paths...}}
Import tag information from extended attributes.

*Method `FSTagsCommand.cmd_xattr_import(self, argv)`*:
Usage: {cmd} {{-|paths...}}
Update extended attributes from tags.

*Method `FSTagsCommand.import_csv_file(self, f, *, convert_name=None)`*:
Import CSV data from the file `f`.

Parameters:
* `f`: the source CSV file
* `convert_name`: a callable to convert each input name
  into a file path; the default is to use the input name directly

*Method `FSTagsCommand.run_context(self)`*:
Push the `FSTags`.

## Class `FSTagsConfig(cs.fs.FSPathBasedSingleton)`

A configuration for fstags.

*Method `FSTagsConfig.__init__(self, rcfilepath=None, physical=None)`*:
Initialise the config.

Parameters:
* `rcfilepath`: the path to the confguration file
  If `None`, default to `'~/.fstagsrc'` (from `RCFILE`).

*Method `FSTagsConfig.cascade_rules_from_config(config)`*:
Return a list of the `[cascade]` tag rules from the config.

*Method `FSTagsConfig.filename_rules_from_config(config)`*:
Return a list of the `[filename_autotag]` tag rules from the config.

*Method `FSTagsConfig.load_config(rcfilepath)`*:
Read an rc file, return a `ConfigParser` instance.

*Property `FSTagsConfig.tagsfile_basename`*:
The tags filename, default `'.fstags'`.

## Class `FSTagsTagFile(cs.tagset.TagFile, HasFSTagsMixin)`

A `FSTagsTagFile` indexing `TagSet`s for file paths
which lives in the file path's directory.

*Method `FSTagsTagFile.TagSetClass(self, name: str) -> cs.fstags.TaggedPath`*:
factory to create a `TaggedPath` from a `name`.

*Property `FSTagsTagFile.dirpath`*:
Return the path of the directory associated with this `FSTagsTagFile`.

## Function `get_xattr_value(fspath, xattr_name)`

Read the extended attribute `xattr_name` of `fspath`.
Return the extended attribute value as a string,
or `None` if the attribute does not exist.

Parameters:
* `fspath`: the filesystem path to update
* `xattr_name`: the extended attribute to obtain
  if this is a `str`, the attribute is the UTF-8 encoding of that name.

## Class `HasFSTagsMixin`

Mixin providing an automatic `.fstags` property.

*Property `HasFSTagsMixin.fstags`*:
Return the `.fstags` property,
default a shared default `FSTags` instance.

## Function `is_valid_basename(name: str)`

Test whether `name` is a valid basefile for something in a directory.

## Function `main(argv=None)`

Command line mode.

## Function `rsync_patterns(paths, top_path)`

Return a list of rsync include lines
suitable for use with the `--include-from` option.

## Class `TaggedPath(cs.tagset.TagSet, HasFSTagsMixin, cs.fs.HasFSPath, cs.deco.Promotable)`

Class to manipulate the tags for a specific path.

*Property `TaggedPath.all_tags`*:
Cached cumulative tags for this path as a `TagSet`
by merging the tags from the root to the path.

Note that subsequent changes to some path component's `direct_tags`
will not affect this `TagSet`.

*Method `TaggedPath.as_tags(self, prefix=None, all_tags=False)`*:
Yield the tag data as `Tag`s.

This overrides `TagSet.as_tags`,
honouring an `optional `all_tags` parameter.

*Method `TaggedPath.auto_infer(self, attr)`*:
Infer a value from `attr` via the associated `FSTags.cascade_rules`.

This implementation tries the cascade rules from the configuration
and falls back to the superclass inference (the direct tag, if present).
Therefore a tagset with a rule for `.title` and also a
direct `.title` Tag would return the direct tag value for
`.title` and the rule value for `.auto.title`.

*Property `TaggedPath.basename`*:
The name of the final path component.

*Method `TaggedPath.export_xattrs(self)`*:
Update the extended attributes of the file.

*Method `TaggedPath.findup(self, check)`*:
Locate the first `TaggedPath` from `self` upwards (via `.parent`)
for which `check(TaggedPath)` is true.

*Method `TaggedPath.format_kwargs(self, *, direct=False)`*:
Format arguments suitable for `str.format_map`.

This returns an `ExtendedNamespace` from `TagSet.ns()`
for a computed `TagSet`.

In addition to the normal `TagSet.ns()` names
the following additional names are available:
* `fspath.basename`: basename of the `TaggedPath.fspath`
* `fspath.pathname`: the `TaggedPath.fspath`
* `fspath.encoded`: the JSON encoded fspath
* `tags`: the `TagSet` as a string

*Method `TaggedPath.format_tagset(self, *, direct=False)`*:
Compute a `TagSet` from this file's tags
with additional derived tags.

This can be converted into an `ExtendedNamespace`
suitable for use with `str.format_map`
via the `TagSet`'s `.format_kwargs()` method.

In addition to the normal `TagSet.ns()` names
the following additional names are available:
* `fspath.basename`: basename of the `TaggedPath.fspath`
* `fspath.ext`: the file extension of the basename
  of the `TaggedPath.fspath`
* `fspath.pathname`: the `TaggedPath.fspath`
* `fspath.encoded`: the JSON encoded fspath

*Method `TaggedPath.from_str(fspath, *, fstags: Optional[cs.fstags.FSTags] = <function <lambda> at 0x10b571090>)`*:
Supports the `@promote` decorator.

*Method `TaggedPath.get_xattr_tagset(self, xattr_name=None)`*:
Return a new `TagSet`
from the extended attribute `xattr_name` of `self.fspath`.
The default `xattr_name` is `XATTR_B` (`None`).

*Method `TaggedPath.import_xattrs(self)`*:
Update the direct tags from the file's extended attributes.

*Method `TaggedPath.infer_from_basename(self, rules=None)`*:
Apply `rules` to the basename of this `TaggedPath`,
return a `TagSet` of inferred `Tag`s.

Tag values from earlier rules override values from later rules.

The default rules come from `self.fstags.config.filename_rules`,
which is sourced from `~/.fstagsrc`.

*Method `TaggedPath.infer_tags(self)`*:
Infer tags for this path.

In order of preference:
* from filesystem fstags
* from file basename matching
* from various `_type` suffixes
* from the cascade rules (see `FSTags.cascade_rules`)

*Property `TaggedPath.keypath`*:
The key path used to index this `TaggedPath` within its `FSTags`
from `FSTags.keypath(path)`.

*Method `TaggedPath.merged_tags(self)`*:
Compute the cumulative tags for this path as a new `TagSet`
by merging the tags from the root to the path.

*Property `TaggedPath.name`*:
The `.name` is `basename(self.fspath)`.

*Property `TaggedPath.parent`*:
A reference to the parent of this `TaggedPath`, or `None`.

*Method `TaggedPath.prune_inherited(self)`*:
Examine the tags of this path's parent.
Remove any tag on this file if they are present on the parent.
Return a `TagSet` containing the pruned `Tag`s.

*Method `TaggedPath.save(self, prune=True)`*:
Update the associated `FSTagsTagFile`.

*Method `TaggedPath.set(self, tag_name, value, **kw)`*:
Forbid the special tag name `'name'`, reserved for the filename.

*Property `TaggedPath.tagfile`*:
Return the `FSTagsTagFile` storing the state for this `TaggedPath`.

## Function `update_xattr_value(fspath, xattr_name, new_xattr_value)`

Update the extended attributes of `fspath`
with `new_xattr_value` for `xattr_name`.
Return the previous value, or `None` if the attribute was missing.

We avoid calling `os.setxattr` if the value will not change.

Parameters:
* `fspath`: the filesystem path to update
* `xattr_name`: the extended attribute to update;
  if this is a `str`, the attribute is the UTF-8 encoding of that name.
* `new_xattr_value`: the new extended attribute value, a `str`
  which should be the transcription of `TagSet`
  i.e. `str(tagset)`

## Function `verbose(msg, *a)`

Emit message if in verbose mode.

# Release Log



*Release 20240422*:
* Bugfix: .fstags files should no longer get Python repr() output for weirdly typed tag values.
* Some other minor internal updates.

*Release 20240412*:
Changes to accomodate dropping BaseCommandOptions.runstate.

*Release 20240316*:
* TaggedPath.infer_tags: add _dt, _date, _f conversions, skip conversions which raise TypeError or ValueError.
* rpaths: fix to yield relative paths.

*Release 20240211*:
* TaggedPath: subclass Promotable, add from_str(fspath) class method.
* FSTags: new mv(srcpath,dstpath) method to move/link/symlink a file bringing its tags.

*Release 20240201.1*:
Release with "fstags" script.

*Release 20240201*:
FSTags.attach_path: do not save the tagfile if the FSTags is open - the final close will do it.

*Release 20231129*:
FSTags.startup_shutdown: run self.sync() in a finally clause.

*Release 20230407*:
* FSTags: support open/close of self.update_mapping, update startup/shutdown to startup_shutdown.
* Move the (optional) ORM open/close from FSTags.startup_shutdown to TagFile.save, greatly shortens the ORM lock.

*Release 20230217*:
FSTagsCommand.cmd_rename: use -o for the format string as for other commands, -n is for "no action".

*Release 20230212*:
* FSTags.keypath(fspath) and TaggedPath.keypath with the singleton filesystem path for a TaggedPath.
* TaggedPath.parent property.
* TaggedPath.findup(check) method.
* FSTagsCommand: cmd_find,cmd_ls: abort if runstate.cancelled thus honouring SIGINT/^C.

*Release 20230211*:
FSTags.__init__: treat empty $FSTAGS_UPDATE_MAPPING as missing i.e. None.

*Release 20230210*:
New optional update_mapping for mirroring tags eg to an SQLTags; activated automatically by an $FSTAGS_UPDATE_MAPPING environment variable.

*Release 20221228*:
TaggedPath.save: new options prune=True parameter to drop empty top level dict/lists.

*Release 20220918*:
* FSTagsConfig: add empty .provided dict for config overrides.
* FSTagsConfig: set FSPATH_DEFAULT=RCFILE for use by FSPathBasedSingleton._singleton_key.
* FSTagsConfig: accept optional `physical` parameter to choose `realpath` over `abspath` and new `.physical` property.
* FSTags.__getitem__: use `abspath` or `realpath` depending on `self.config.physical`.
* FSTagsCommand: new -P option for "physical" mode, plumb though to the config and self.options.
* Provide a DEFAULT_FSTAGS instance and an @uses_fstags decorator.

*Release 20220606*:
FSTagsCommand.edit: mention the filename in the header comments in tagset edit mode.

*Release 20220430*:
Minor bugfixes.

*Release 20220311*:
* New TaggedPath.infer_tags() method to compute and return the inferred TagSet.
* New "fstags infer pathname" command.

*Release 20211212*:
* Rename edit_many to edit_tagsets for clarity.
* FSTags.edit_dirpath: include realpath(dirpath) at the top of the edit list.

*Release 20210906*:
* TaggedPath: new auto_infer method overriding the inherited TagSet.auto_infer which consults the cascade_rules from the .fstagsrc.
* fstags ls: new -l option to print a multiline tag listing.
* fstags cptags: copy the tags from one path to another.
* Assorted other changes.

*Release 20210404*:
* FSTags.edit_dirpath: new all_names parameter to include dot-names.
* FSTagsCommand.cmd_edit: new -a option to turn on the all_names parameter.

*Release 20210306*:
* FSTags: new tagfile_for(filepath) to obtain the TagFile for filepath.
* FSTagsCommand: new -o ontology option to supply an ontology file for the FSTags.
* FSTagsCommand.cmd_ont: drop -o/--ontology, superceded by global -o option.
* Move BaseTagFile from cs.fstags to TagFile in cs.tagset.
* FSTags.edit_dirpath: we now get (old_name,new_name,TaggedPath) back from edit_many, obviating the te_id_map.
* TaggedPath: .name property returning basename(self.filepath), .set and .discard methods rejecting use of the 'name' tag.
* FSTagsCommand: port to new cs.cmdutils API.
* Many small refactors and bugfixes.

*Release 20200717.1*:
Add the manual page MarkDown source files. Note: well out of date, need updating.

*Release 20200717*:
DISTINFO: require cs.obj>=20200716 for SingletonMixin API change.

*Release 20200716*:
* Update for changed cs.obj.SingletonMixin API.
* fstags: new "export" and "import" subcommands roughly paralleling those from "sqltags", providing CSV formatted export/import.
* fstags export: ew -a (all paths) option - the default is not to not export paths with no tags.

*Release 20200521.1*:
fix DISTINFO.install_requires

*Release 20200521*:
* Add -i option to cp,ln,mv for command line compatibility, just disables -f.
* New "rename" subcommand to rename files according to a format string.
* Ontology support (optional).
* Various classes are now singletons to avoid dissonance.
* Fold "edittags" subcommand into "edit" via the "-d" (directories like files) option.
* New "ns" subcommand reporting on the primary names available for formatting.
* Accept [clausename]entryname as a format string to obtain the string from that entry of the config file.
* Many bugfixes and improvements.

*Release 20200229*:
* New TaggedPath.modified property aliasing the TagSet.modified attribute.
* ls: new -d option to treat directories like files (do not recurse), aiding reporting of tags for a directory.
* find,ls subcommands: work off the realpath of the supplied top level path.
* Tag: now subclasses namedtuple.
* Rewrite rpaths() to use scandir and to also yield (is_dir,path) tuples.
* TagSet, Tag, TagChoice moved into new cs.tagset module for reuse.
* json_import: make --prefix mandatory, is "." as separator if not empty.
* Move filename regexp rules to [filename_rules] config section.
* New CascadeRule for representing a "target_tag_name = tag_name1 tag_name2..." config rules.
* autotag: include the cascade rules in the autotagging after the filename rules.

*Release 20200210*:
* New "json_import" subcommand to import a JSON dict as tags, initial use case to load the metadata from youtube-dl.
* New "scrub" command line operation, to purge tags of paths which do not exist.
* New "cp", "ln" and "mv" subcommands to copy/link/move paths and take their tags with them.
* New "test" subcommand to test paths against tag criteria, useful for find and scripts.
* Small bugfixes.

*Release 20200130*:
* New FSTagsConfig class which parses the .fstagsrc as a .ini file; related adjustments.
* New HasFSTagsMixin presenting a settable .fstags property with a shared default.
* New xattr_import and xattr_export subcommands, remove implicit xattr access/update from other operations.
* New TagSet.__len__ returning the number of tags.
* Add "-" support for stdin to "tag" and "tagpaths" subcommands.

*Release 20200113.2*:
FSTagsCommand docstring tweak.

*Release 20200113.1*:
Small docstring updates.

*Release 20200113*:
Mirror tags to user.cs.fstags xattr to honour Linux namespace requirements. Add "filesize" to available tag string format (-o option). Small bugfixes.

*Release 20191230*:
* Command line: new "find" command to search a file tree based on tags.
* Command line: new "mv" command to move a file and its tags.
* Command line: Python string formats for "find" and "ls" output.
* TaggedPath.autotag: new optional `no_save` parameter, default False, to suppress update of the associated .fstags file.
* Inital and untested "mirror tags to xattrs" support.

*Release 20191201*:
New "edit" subcommand to rename files and edit tags.

*Release 20191130.1*:
Initial release: fstags, filesystem based tagging utility.


            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "cs.fstags",
    "maintainer": null,
    "docs_url": null,
    "requires_python": null,
    "maintainer_email": null,
    "keywords": "python3",
    "author": null,
    "author_email": "Cameron Simpson <cs@cskk.id.au>",
    "download_url": "https://files.pythonhosted.org/packages/c2/ce/2d7791f172f5bcd4f4dd12821ba2cd84502e88f3f023ae249636b5aee0d0/cs.fstags-20240422.tar.gz",
    "platform": null,
    "description": "Simple filesystem based file tagging\nand the associated `fstags` command line script.\n\n*Latest release 20240422*:\n* Bugfix: .fstags files should no longer get Python repr() output for weirdly typed tag values.\n* Some other minor internal updates.\n\nMany basic tasks can be performed with the `fstags` command line utility,\ndocumented under the `FSTagsCommand` class below.\n\nWhy `fstags`?\nBy storing the tags in a separate file we:\n* can store tags without modifying a file\n* do not need to know the file's format,\n  or even whether that format supports metadata\n* can process tags on any kind of file\n* because tags are inherited from parent directories,\n  tags can be automatically acquired merely by arranging your file tree\n\nTags are stored in the file `.fstags` in each directory;\nthere is a line for each entry in the directory\nconsisting of the directory entry name and the associated tags.\n\nTags may be \"bare\", or have a value.\nIf there is a value it is expressed with an equals (`'='`)\nfollowed by the JSON encoding of the value.\n\nThe tags for a file are the union of its direct tags\nand all relevant ancestor tags,\nwith priority given to tags closer to the file.\n\nFor example, a media file for a television episode with the pathname\n`/path/to/series-name/season-02/episode-name--s02e03--something.mp4`\nmight have the tags:\n\n    series_title=\"Series Full Name\"\n    season=2\n    sf\n    episode=3\n    episode_title=\"Full Episode Title\"\n\nobtained from the following `.fstags` entries:\n* tag file `/path/to/.fstags`:\n\n    series-name sf series_title=\"Series Full Name\"\n\n* tag file `/path/to/series-name/.fstags`:\n\n  season-02 season=2\n\n* tag file `/path/to/series-name/season-02/.fstags`:\n\n  episode-name--s02e03--something.mp4 episode=3 episode_title=\"Full Episode Title\"\n\n## `fstags` Examples ##\n\n### Backing up a media tree too big for the removable drives ###\n\nWalk the media tree for files tagged for backup to `archive2`:\n\n    fstags find /path/to/media backup=archive2\n\nWalk the media tree for files not assigned to a backup archive:\n\n    fstags find /path/to/media -backup\n\nBackup the `archive2` files using `rsync`:\n\n    fstags find --for-rsync /path/to/media backup=archive2 \\\n    | rsync -ia --include-from=- /path/to/media /path/to/backup_archive2\n\n## Class `CascadeRule`\n\nA cascade rule of possible source tag names to provide a target tag.\n\n*Method `CascadeRule.infer_tag(self, tagset)`*:\nApply the rule to the `TagSet` `tagset`.\nReturn a new `Tag(self.target,value)`\nfor the first cascade `value` found in `tagset`,\nor `None` if there is no match.\n\n## `DEFAULT_FSTAGS = FSTags('.fstags')`\n\nA class to examine filesystem tags.\n\n## Class `FSTags(cs.resources.MultiOpenMixin)`\n\nA class to examine filesystem tags.\n\n*Method `FSTags.__init__(self, tagsfile_basename=None, ontology_filepath=None, physical=None, update_mapping: Optional[Mapping] = None, update_prefix: Optional[str] = 'cs.fstags', update_uuid_tag_name: Optional[str] = 'uuid')`*:\nInitialise the `FSTags` instance.\n\nParameters:\n* `tagsfile_basename`: optional basename forthe backing tags files,\n  default from `TAGSFILE_BASENAME`: `'.fstags'`\n* `ontology_filepath`: optional filesystem path for an associated ontology\n* `physical`: optional flag for the associated `FSTagsConfig`\n  specifying whether `TagFile`s are indexed by their physical or logical\n  filesystem paths\n* `update_mapping`: optional secondary mapping to which to mirror\n  tags, such as an `SQLTags`;\n  the default comes from an `SQLTags` specified by the\n  environment variable `$FSTAGS_UPDATE_MAPPING`\n  if present\n* `update_prefix`: optional key prefix for use in the secondary mapping;\n  the default comes from the environment variable\n  `$FSTAGS_UPDATE_MAPPING_PREFIX` if present,\n  otherwise `'cs.fstags'`\n* `update_uuid_tag_name`: optional name for the per file UUID tag name;\n  default `'uuid'`\n\n*Method `FSTags.__getitem__(self, path) -> 'TaggedPath'`*:\nReturn the `TaggedPath` for `abspath(path)`.\n\n*Method `FSTags.apply_tag_choices(self, tag_choices, paths)`*:\nApply the `tag_choices` to `paths`.\n\nParameters:\n* `tag_choices`:\n  an iterable of `Tag` or an equality `TagBasedTest`.\n  Each item applies or removes a `Tag`\n  from each path's direct tags.\n* `paths`:\n  an iterable of filesystem paths.\n\n*Method `FSTags.attach_path(self, attach, srcpath, dstpath, *, force=False, crop_ok=False)`*:\nAttach `srcpath` to `dstpath` using the `attach` callable.\n\nParameters:\n* `attach`: callable accepting `attach(srcpath,dstpath)`\n  to do the desired attachment,\n  such as a copy, link or move\n* `srcpath`: the source filesystem object\n* `dstpath`: the destination filesystem object\n* `crop_ok`: if true and the OS raises `OSError(ENAMETOOLONG)`\n  attempt to crop the name before the file extension and retry\n* `force`: default `False`.\n  If true and the destination exists\n  try to remove it before calling `attach`.\n  Otherwise if the destination exists\n  raise a `ValueError`.\n\n*Method `FSTags.cascade_tags(self, tags, cascade_rules=None)`*:\nYield `Tag`s\nwhich cascade from the `TagSet` `tags`\nvia `cascade_rules` (an iterable of `CascadeRules`).\n\n*Method `FSTags.copy(self, srcpath, dstpath, **kw)`*:\nCopy `srcpath` to `dstpath`.\n\n*Method `FSTags.dir_tagfile(self, dirpath: str) -> 'FSTagsTagFile'`*:\nReturn the `FSTagsTagFile` associated with `dirpath`.\n\n*Method `FSTags.edit_dirpath(self, dirpath, all_names=False)`*:\nEdit the filenames and tags in a directory.\n\nIf `all_names` is true, include names commencing with a dot,\notherwise exclude them.\n\n*Method `FSTags.export_xattrs(self, paths)`*:\nImport the extended attributes of `paths`\nand use them to update the fstags.\n\n*Method `FSTags.find(self, path, tag_tests, use_direct_tags=False)`*:\nWalk the file tree from `path`\nsearching for files matching the supplied `tag_tests`.\nYield the matching file paths.\n\nParameters:\n* `path`: the top of the file tree to walk\n* `tag_tests`: a sequence of `TagBasedTest`s\n* `use_direct_tags`: test the `direct_tags` if true,\n  otherwise the `all_tags`.\n  Default: `False`\n\n*Method `FSTags.find_ontology(self, dirpath, ontbase=None)`*:\nLocate an ontology for the directory `dirpath`.\nThe optional `ontbase` may override the relative path to the file,\ndefault is `self.ontology_filepath`.\nReturn a `TagOntology` or `None` if not found.\n\n*Method `FSTags.find_ontpath(self, dirpath, ontbase=None)`*:\nLocate an ontology file for the directory `dirpath`.\nThe optional `ontbase` may override the relative path to the file,\ndefault is `self.ontology_filepath`.\nReturn the found ontology file or `None` if not found.\n\n*Method `FSTags.import_xattrs(self, paths)`*:\nUpdate the extended attributes of `paths`\nfrom their fstags.\n\n*Method `FSTags.keypath(self, fspath)`*:\nCompute the absolute path used to index a `TaggedPath` instance.\n\nThis returns `realpath(fspath)` if `self.config.physical`,\notherwise `abspath(fspath)`.\n\n*Method `FSTags.link(self, srcpath, dstpath, **kw)`*:\nLink `srcpath` to `dstpath`.\n\n*Method `FSTags.move(self, srcpath, dstpath, **kw)`*:\nMove `srcpath` to `dstpath`.\n\n*Method `FSTags.mv(self, srcpath: str, dstpath: str, *, symlink=False, remove=True)`*:\nMove (or link or symlink) `srcpath` to `dstpath`.\n\nParameters:\n* `srcpath`: the source filesystem path\n* `dstpath`: the destination filesystem path\n* `symlink`: default `False`: if true, make a symbolic link\n* `remove`: default `True`: if true, remove `srcpath` after\n  hard linking to `dstpath`\n\n*Property `FSTags.ontology`*:\nThe primary `TagsOntology`, or `None` if `self.ontology_filepath` was `None`.\n\n*Property `FSTags.ontology_filepath`*:\nThe ontology file basename.\n\n*Method `FSTags.ontology_for(self, path, ontbase=None)`*:\nReturn the `TagsOntology` associated with `path`.\nReturns `None` if an ontology cannot be found.\n\n*Method `FSTags.open_ontology(self, ontpath)`*:\nOpen the contology file at `ontpath`.\n\n*Method `FSTags.path_tagfiles(self, fspath)`*:\nGenerator yielding a sequence of `(FSTagsTagFile,name)` pairs\nwhere `name` is the key within the `FSTagsTagFile`\nfor the `FSTagsTagFile`s affecting `fspath`\nin order from the root to `dirname(fspath)`.\n\n*Method `FSTags.resolve_format_string(self, format_string)`*:\nSee if `format_string` looks like `[`*clausename*`]`*entryname*.\nif so, return the corresponding config entry string,\notherwise return `format_string` unchanged.\n\n*Method `FSTags.scrub(self, path)`*:\nScrub tags for names which do not exist in the filesystem.\n\n*Method `FSTags.startup_shutdown(self)`*:\nSync tag files and db mapping on final close.\n\n*Method `FSTags.sync(self)`*:\nFlush modified tag files.\n\n*Method `FSTags.tagfile_for(self, fspath)`*:\nReturn the `FSTagsTagFile` storing the `Tag`s for `fspath`.\n\n*Property `FSTags.tagsfile_basename`*:\nThe tag file basename.\n\n*Method `FSTags.test(self, path, tag_tests, use_direct_tags=False)`*:\nTest a path against `tag_tests`.\n\nParameters:\n* `path`: path to test\n* `tag_tests`: an iterable of `TagBasedTest`s\n* `use_direct_tags`: test the `direct_tags` if true,\n  otherwise the `all_tags`.\n  Default: `False`\n\n## Class `FSTagsCommand(cs.cmdutils.BaseCommand, cs.tagset.TagsCommandMixin)`\n\n`fstags` main command line utility.\n\nCommand line implementation.\n\nUsage summary:\n\n    Usage: fstags [-o ontology] [-P] subcommand [...]\n      -o ontology   Specify the path to an ontology file.\n      -P            Physical. Resolve pathnames through symlinks.\n                    Default ~/.fstagsrc[general]physical or False.\n      Subcommands:\n        autotag paths...\n          Tag paths based on rules from the rc file.\n        cp [-finv] srcpath dstpath, cp [-finv] srcpaths... dstdirpath\n          POSIX cp(1) equivalent, but also copying tags:\n          copy files and their tags into targetdir.\n          -f  Force: remove destination if it exists.\n          -i  Interactive: fail if the destination exists.\n          -n  No remove: fail if the destination exists.\n          -v  Verbose: show copied files.\n        cptags srcpath dstpath\n          Copy the direct tags from srcpath to dstpath.\n        edit [-ad] [path]\n          Edit the direct tagsets of path, default: '.'\n          If path is a directory, provide the tags of its entries.\n          Otherwise edit just the tags for path.\n          -a    List all names in directory edit mode; normally\n                names commencing with a dot are omitted.\n          -d    Treat directories like files: edit just its tags.\n        export [-a] [--direct] path {tag[=value]|-tag}...\n          Export tags for files from paths matching all the constraints.\n          -a        Export all paths, not just those with tags.\n          --direct  Export the direct tags instead of the computed tags.\n          The output is in the same CSV format as that from \"sqltags export\",\n          with the following columns:\n          * unixtime: the file's st_mtime from os.stat.\n          * id: empty\n          * name: the file path\n          * tags: the file's direct or indirect tags\n        find [--direct] [--for-rsync] [-o output_format] path {tag[=value]|-tag}...\n          List files from path matching all the constraints.\n          --direct    Use direct tags instead of all tags.\n          --for-rsync Instead of listing matching paths, emit a\n                      sequence of rsync(1) patterns suitable for use with\n                      --include-from in order to do a selective rsync of the\n                      matched paths.\n          -o output_format\n                      Use output_format as a Python format string to lay out\n                      the listing.\n                      Default: {fspath}\n        help [-l] [subcommand-names...]\n          Print help for subcommands.\n          This outputs the full help for the named subcommands,\n          or the short help for all subcommands if no names are specified.\n          -l  Long help even if no subcommand-names provided.\n        import {-|srcpath}...\n          Import CSV data in the format emitted by \"export\".\n          Each argument is a file path or \"-\", indicating standard input.\n        infer pathname\n          Print the base and inferred tags for pathname.\n        json_import --prefix=tag_prefix {-|path} {-|tags.json}\n          Apply JSON data to path.\n          A path named \"-\" indicates that paths should be read from\n          the standard input.\n          The JSON tag data come from the file \"tags.json\"; the name\n          \"-\" indicates that the JSON data should be read from the\n          standard input.\n        ln [-finv] srcpath dstpath, ln [-finv] srcpaths... dstdirpath\n          POSIX ln(1) equivalent, but also copying the tags:\n          link files and their tags into targetdir.\n          -f  Force: remove destination if it exists.\n          -i  Interactive: fail if the destination exists.\n          -n  No remove: fail if the destination exists.\n          -v  Verbose: show linked files.\n        ls [-dlr] [--direct] [-o output_format] [paths...]\n          List files from paths and their tags.\n          -d          Treat directories like files, do not recurse.\n          --direct    List direct tags instead of all tags.\n          -l          Long format.\n          -o output_format\n                      Use output_format as a Python format string to lay out\n                      the listing.\n                      Default: {fspath:json} {tags}\n          -r          Recurse into subdirectories.\n        mv [-finv] srcpath dstpath, mv [-finv] srcpaths... dstdirpath\n          POSIX mv(1) equivalent, but also copying the tags:\n          move files and their tags into targetdir.\n          -f  Force: remove destination if it exists.\n          -i  Interactive: fail if the destination exists.\n          -n  No remove: fail if the destination exists.\n          -v  Verbose: show moved files.\n        ns [-d] [--direct] [paths...]\n          Report on the available primary namespace fields for formatting.\n          Note that because the namespace used for formatting has\n          inferred field names there are also unshown secondary field\n          names available in format strings.\n          -d          Treat directories like files, do not recurse.\n          --direct    List direct tags instead of all tags.\n        ont [subcommand [args...]]\n          With no arguments, print the ontology.\n        rename -o basename_format paths...\n          Rename paths according to a format string.\n          -o basename_format\n              Use basename_format as a Python format string to\n              compute the new basename for each path.\n        scrub paths...\n          Remove all tags for missing paths.\n          If a path is a directory, scrub the immediate paths in the directory.\n        shell\n          Run a command prompt via cmd.Cmd using this command's subcommands.\n        tag {-|path} {tag[=value]|-tag}...\n          Tag a path with multiple tags.\n          With the form \"-tag\", remove that tag from the direct tags.\n          A path named \"-\" indicates that paths should be read from the\n          standard input.\n        tagfile tagfile_path [subcommand ...]\n          Subcommands:\n            tag tagset_name {tag[=value]|-tag}...\n              Directly modify tag_name within the tag file tagfile_path.\n        tagpaths {tag[=value]|-tag} {-|paths...}\n          Tag multiple paths.\n          With the form \"-tag\", remove the tag from the immediate tags.\n          A single path named \"-\" indicates that paths should be read\n          from the standard input.\n        test [--direct] path {tag[=value]|-tag}...\n          Test whether the path matches all the constraints.\n          --direct    Use direct tags instead of all tags.\n        xattr_export {-|paths...}\n          Import tag information from extended attributes.\n        xattr_import {-|paths...}\n          Update extended attributes from tags.\n\n*`FSTagsCommand.Options`*\n\n*Method `FSTagsCommand.apply_opt(self, opt, val)`*:\nApply command line option.\n\n*Method `FSTagsCommand.cmd_autotag(self, argv, *, upd: cs.upd.Upd, runstate: Optional[cs.resources.RunState] = <function <lambda> at 0x10b33df30>)`*:\nUsage: {cmd} paths...\nTag paths based on rules from the rc file.\n\n*Method `FSTagsCommand.cmd_cp(self, argv)`*:\nUsage: {cmd} [-finv] srcpath dstpath, {cmd} [-finv] srcpaths... dstdirpath\nPOSIX cp(1) equivalent, but also copying tags:\ncopy files and their tags into targetdir.\n-f  Force: remove destination if it exists.\n-i  Interactive: fail if the destination exists.\n-n  No remove: fail if the destination exists.\n-v  Verbose: show copied files.\n\n*Method `FSTagsCommand.cmd_cptags(self, argv)`*:\nUsage: {cmd} srcpath dstpath\nCopy the direct tags from srcpath to dstpath.\n\n*Method `FSTagsCommand.cmd_edit(self, argv)`*:\nUsage: {cmd} [-ad] [path]\nEdit the direct tagsets of path, default: '.'\nIf path is a directory, provide the tags of its entries.\nOtherwise edit just the tags for path.\n-a    List all names in directory edit mode; normally\n      names commencing with a dot are omitted.\n-d    Treat directories like files: edit just its tags.\n\n*Method `FSTagsCommand.cmd_export(self, argv, *, runstate: Optional[cs.resources.RunState] = <function <lambda> at 0x10b33df30>)`*:\nUsage: {cmd} [-a] [--direct] path {{tag[=value]|-tag}}...\nExport tags for files from paths matching all the constraints.\n-a        Export all paths, not just those with tags.\n--direct  Export the direct tags instead of the computed tags.\nThe output is in the same CSV format as that from \"sqltags export\",\nwith the following columns:\n* unixtime: the file's st_mtime from os.stat.\n* id: empty\n* name: the file path\n* tags: the file's direct or indirect tags\n\n*Method `FSTagsCommand.cmd_find(self, argv, *, runstate: Optional[cs.resources.RunState] = <function <lambda> at 0x10b33df30>)`*:\nUsage: {cmd} [--direct] [--for-rsync] [-o output_format] path {{tag[=value]|-tag}}...\nList files from path matching all the constraints.\n--direct    Use direct tags instead of all tags.\n--for-rsync Instead of listing matching paths, emit a\n            sequence of rsync(1) patterns suitable for use with\n            --include-from in order to do a selective rsync of the\n            matched paths.\n-o output_format\n            Use output_format as a Python format string to lay out\n            the listing.\n            Default: {FIND_OUTPUT_FORMAT_DEFAULT}\n\n*Method `FSTagsCommand.cmd_import(self, argv)`*:\nUsage: {cmd} {{-|srcpath}}...\nImport CSV data in the format emitted by \"export\".\nEach argument is a file path or \"-\", indicating standard input.\n\n*Method `FSTagsCommand.cmd_infer(self, argv)`*:\nUsage: {cmd} pathname\nPrint the base and inferred tags for pathname.\n\n*Method `FSTagsCommand.cmd_json_import(self, argv)`*:\nUsage: json_import --prefix=tag_prefix {{-|path}} {{-|tags.json}}\nApply JSON data to path.\nA path named \"-\" indicates that paths should be read from\nthe standard input.\nThe JSON tag data come from the file \"tags.json\"; the name\n\"-\" indicates that the JSON data should be read from the\nstandard input.\n\n*Method `FSTagsCommand.cmd_ln(self, argv)`*:\nUsage: {cmd} [-finv] srcpath dstpath, {cmd} [-finv] srcpaths... dstdirpath\nPOSIX ln(1) equivalent, but also copying the tags:\nlink files and their tags into targetdir.\n-f  Force: remove destination if it exists.\n-i  Interactive: fail if the destination exists.\n-n  No remove: fail if the destination exists.\n-v  Verbose: show linked files.\n\n*Method `FSTagsCommand.cmd_ls(self, argv, *, runstate: Optional[cs.resources.RunState] = <function <lambda> at 0x10b33df30>)`*:\nUsage: {cmd} [-dlr] [--direct] [-o output_format] [paths...]\nList files from paths and their tags.\n-d          Treat directories like files, do not recurse.\n--direct    List direct tags instead of all tags.\n-l          Long format.\n-o output_format\n            Use output_format as a Python format string to lay out\n            the listing.\n            Default: {LS_OUTPUT_FORMAT_DEFAULT}\n-r          Recurse into subdirectories.\n\n*Method `FSTagsCommand.cmd_mv(self, argv)`*:\nUsage: {cmd} [-finv] srcpath dstpath, {cmd} [-finv] srcpaths... dstdirpath\nPOSIX mv(1) equivalent, but also copying the tags:\nmove files and their tags into targetdir.\n-f  Force: remove destination if it exists.\n-i  Interactive: fail if the destination exists.\n-n  No remove: fail if the destination exists.\n-v  Verbose: show moved files.\n\n*Method `FSTagsCommand.cmd_ns(self, argv)`*:\nUsage: {cmd} [-d] [--direct] [paths...]\nReport on the available primary namespace fields for formatting.\nNote that because the namespace used for formatting has\ninferred field names there are also unshown secondary field\nnames available in format strings.\n-d          Treat directories like files, do not recurse.\n--direct    List direct tags instead of all tags.\n\n*Method `FSTagsCommand.cmd_ont(self, argv)`*:\nOntology operations.\n\nUsage: {cmd} [subcommand [args...]]\n  With no arguments, print the ontology.\n\n*Method `FSTagsCommand.cmd_rename(self, argv)`*:\nUsage: {cmd} -o basename_format paths...\nRename paths according to a format string.\n-o basename_format\n    Use basename_format as a Python format string to\n    compute the new basename for each path.\n\n*Method `FSTagsCommand.cmd_scrub(self, argv)`*:\nUsage: {cmd} paths...\nRemove all tags for missing paths.\nIf a path is a directory, scrub the immediate paths in the directory.\n\n*Method `FSTagsCommand.cmd_tag(self, argv)`*:\nUsage: {cmd} {{-|path}} {{tag[=value]|-tag}}...\nTag a path with multiple tags.\nWith the form \"-tag\", remove that tag from the direct tags.\nA path named \"-\" indicates that paths should be read from the\nstandard input.\n\n*Method `FSTagsCommand.cmd_tagfile(self, argv)`*:\nUsage: {cmd} tagfile_path [subcommand ...]\nSubcommands:\n  tag tagset_name {{tag[=value]|-tag}}...\n    Directly modify tag_name within the tag file tagfile_path.\n\n*Method `FSTagsCommand.cmd_tagpaths(self, argv)`*:\nUsage: {cmd} {{tag[=value]|-tag}} {{-|paths...}}\nTag multiple paths.\nWith the form \"-tag\", remove the tag from the immediate tags.\nA single path named \"-\" indicates that paths should be read\nfrom the standard input.\n\n*Method `FSTagsCommand.cmd_test(self, argv)`*:\nUsage: {cmd} [--direct] path {{tag[=value]|-tag}}...\nTest whether the path matches all the constraints.\n--direct    Use direct tags instead of all tags.\n\n*Method `FSTagsCommand.cmd_xattr_export(self, argv)`*:\nUsage: {cmd} {{-|paths...}}\nImport tag information from extended attributes.\n\n*Method `FSTagsCommand.cmd_xattr_import(self, argv)`*:\nUsage: {cmd} {{-|paths...}}\nUpdate extended attributes from tags.\n\n*Method `FSTagsCommand.import_csv_file(self, f, *, convert_name=None)`*:\nImport CSV data from the file `f`.\n\nParameters:\n* `f`: the source CSV file\n* `convert_name`: a callable to convert each input name\n  into a file path; the default is to use the input name directly\n\n*Method `FSTagsCommand.run_context(self)`*:\nPush the `FSTags`.\n\n## Class `FSTagsConfig(cs.fs.FSPathBasedSingleton)`\n\nA configuration for fstags.\n\n*Method `FSTagsConfig.__init__(self, rcfilepath=None, physical=None)`*:\nInitialise the config.\n\nParameters:\n* `rcfilepath`: the path to the confguration file\n  If `None`, default to `'~/.fstagsrc'` (from `RCFILE`).\n\n*Method `FSTagsConfig.cascade_rules_from_config(config)`*:\nReturn a list of the `[cascade]` tag rules from the config.\n\n*Method `FSTagsConfig.filename_rules_from_config(config)`*:\nReturn a list of the `[filename_autotag]` tag rules from the config.\n\n*Method `FSTagsConfig.load_config(rcfilepath)`*:\nRead an rc file, return a `ConfigParser` instance.\n\n*Property `FSTagsConfig.tagsfile_basename`*:\nThe tags filename, default `'.fstags'`.\n\n## Class `FSTagsTagFile(cs.tagset.TagFile, HasFSTagsMixin)`\n\nA `FSTagsTagFile` indexing `TagSet`s for file paths\nwhich lives in the file path's directory.\n\n*Method `FSTagsTagFile.TagSetClass(self, name: str) -> cs.fstags.TaggedPath`*:\nfactory to create a `TaggedPath` from a `name`.\n\n*Property `FSTagsTagFile.dirpath`*:\nReturn the path of the directory associated with this `FSTagsTagFile`.\n\n## Function `get_xattr_value(fspath, xattr_name)`\n\nRead the extended attribute `xattr_name` of `fspath`.\nReturn the extended attribute value as a string,\nor `None` if the attribute does not exist.\n\nParameters:\n* `fspath`: the filesystem path to update\n* `xattr_name`: the extended attribute to obtain\n  if this is a `str`, the attribute is the UTF-8 encoding of that name.\n\n## Class `HasFSTagsMixin`\n\nMixin providing an automatic `.fstags` property.\n\n*Property `HasFSTagsMixin.fstags`*:\nReturn the `.fstags` property,\ndefault a shared default `FSTags` instance.\n\n## Function `is_valid_basename(name: str)`\n\nTest whether `name` is a valid basefile for something in a directory.\n\n## Function `main(argv=None)`\n\nCommand line mode.\n\n## Function `rsync_patterns(paths, top_path)`\n\nReturn a list of rsync include lines\nsuitable for use with the `--include-from` option.\n\n## Class `TaggedPath(cs.tagset.TagSet, HasFSTagsMixin, cs.fs.HasFSPath, cs.deco.Promotable)`\n\nClass to manipulate the tags for a specific path.\n\n*Property `TaggedPath.all_tags`*:\nCached cumulative tags for this path as a `TagSet`\nby merging the tags from the root to the path.\n\nNote that subsequent changes to some path component's `direct_tags`\nwill not affect this `TagSet`.\n\n*Method `TaggedPath.as_tags(self, prefix=None, all_tags=False)`*:\nYield the tag data as `Tag`s.\n\nThis overrides `TagSet.as_tags`,\nhonouring an `optional `all_tags` parameter.\n\n*Method `TaggedPath.auto_infer(self, attr)`*:\nInfer a value from `attr` via the associated `FSTags.cascade_rules`.\n\nThis implementation tries the cascade rules from the configuration\nand falls back to the superclass inference (the direct tag, if present).\nTherefore a tagset with a rule for `.title` and also a\ndirect `.title` Tag would return the direct tag value for\n`.title` and the rule value for `.auto.title`.\n\n*Property `TaggedPath.basename`*:\nThe name of the final path component.\n\n*Method `TaggedPath.export_xattrs(self)`*:\nUpdate the extended attributes of the file.\n\n*Method `TaggedPath.findup(self, check)`*:\nLocate the first `TaggedPath` from `self` upwards (via `.parent`)\nfor which `check(TaggedPath)` is true.\n\n*Method `TaggedPath.format_kwargs(self, *, direct=False)`*:\nFormat arguments suitable for `str.format_map`.\n\nThis returns an `ExtendedNamespace` from `TagSet.ns()`\nfor a computed `TagSet`.\n\nIn addition to the normal `TagSet.ns()` names\nthe following additional names are available:\n* `fspath.basename`: basename of the `TaggedPath.fspath`\n* `fspath.pathname`: the `TaggedPath.fspath`\n* `fspath.encoded`: the JSON encoded fspath\n* `tags`: the `TagSet` as a string\n\n*Method `TaggedPath.format_tagset(self, *, direct=False)`*:\nCompute a `TagSet` from this file's tags\nwith additional derived tags.\n\nThis can be converted into an `ExtendedNamespace`\nsuitable for use with `str.format_map`\nvia the `TagSet`'s `.format_kwargs()` method.\n\nIn addition to the normal `TagSet.ns()` names\nthe following additional names are available:\n* `fspath.basename`: basename of the `TaggedPath.fspath`\n* `fspath.ext`: the file extension of the basename\n  of the `TaggedPath.fspath`\n* `fspath.pathname`: the `TaggedPath.fspath`\n* `fspath.encoded`: the JSON encoded fspath\n\n*Method `TaggedPath.from_str(fspath, *, fstags: Optional[cs.fstags.FSTags] = <function <lambda> at 0x10b571090>)`*:\nSupports the `@promote` decorator.\n\n*Method `TaggedPath.get_xattr_tagset(self, xattr_name=None)`*:\nReturn a new `TagSet`\nfrom the extended attribute `xattr_name` of `self.fspath`.\nThe default `xattr_name` is `XATTR_B` (`None`).\n\n*Method `TaggedPath.import_xattrs(self)`*:\nUpdate the direct tags from the file's extended attributes.\n\n*Method `TaggedPath.infer_from_basename(self, rules=None)`*:\nApply `rules` to the basename of this `TaggedPath`,\nreturn a `TagSet` of inferred `Tag`s.\n\nTag values from earlier rules override values from later rules.\n\nThe default rules come from `self.fstags.config.filename_rules`,\nwhich is sourced from `~/.fstagsrc`.\n\n*Method `TaggedPath.infer_tags(self)`*:\nInfer tags for this path.\n\nIn order of preference:\n* from filesystem fstags\n* from file basename matching\n* from various `_type` suffixes\n* from the cascade rules (see `FSTags.cascade_rules`)\n\n*Property `TaggedPath.keypath`*:\nThe key path used to index this `TaggedPath` within its `FSTags`\nfrom `FSTags.keypath(path)`.\n\n*Method `TaggedPath.merged_tags(self)`*:\nCompute the cumulative tags for this path as a new `TagSet`\nby merging the tags from the root to the path.\n\n*Property `TaggedPath.name`*:\nThe `.name` is `basename(self.fspath)`.\n\n*Property `TaggedPath.parent`*:\nA reference to the parent of this `TaggedPath`, or `None`.\n\n*Method `TaggedPath.prune_inherited(self)`*:\nExamine the tags of this path's parent.\nRemove any tag on this file if they are present on the parent.\nReturn a `TagSet` containing the pruned `Tag`s.\n\n*Method `TaggedPath.save(self, prune=True)`*:\nUpdate the associated `FSTagsTagFile`.\n\n*Method `TaggedPath.set(self, tag_name, value, **kw)`*:\nForbid the special tag name `'name'`, reserved for the filename.\n\n*Property `TaggedPath.tagfile`*:\nReturn the `FSTagsTagFile` storing the state for this `TaggedPath`.\n\n## Function `update_xattr_value(fspath, xattr_name, new_xattr_value)`\n\nUpdate the extended attributes of `fspath`\nwith `new_xattr_value` for `xattr_name`.\nReturn the previous value, or `None` if the attribute was missing.\n\nWe avoid calling `os.setxattr` if the value will not change.\n\nParameters:\n* `fspath`: the filesystem path to update\n* `xattr_name`: the extended attribute to update;\n  if this is a `str`, the attribute is the UTF-8 encoding of that name.\n* `new_xattr_value`: the new extended attribute value, a `str`\n  which should be the transcription of `TagSet`\n  i.e. `str(tagset)`\n\n## Function `verbose(msg, *a)`\n\nEmit message if in verbose mode.\n\n# Release Log\n\n\n\n*Release 20240422*:\n* Bugfix: .fstags files should no longer get Python repr() output for weirdly typed tag values.\n* Some other minor internal updates.\n\n*Release 20240412*:\nChanges to accomodate dropping BaseCommandOptions.runstate.\n\n*Release 20240316*:\n* TaggedPath.infer_tags: add _dt, _date, _f conversions, skip conversions which raise TypeError or ValueError.\n* rpaths: fix to yield relative paths.\n\n*Release 20240211*:\n* TaggedPath: subclass Promotable, add from_str(fspath) class method.\n* FSTags: new mv(srcpath,dstpath) method to move/link/symlink a file bringing its tags.\n\n*Release 20240201.1*:\nRelease with \"fstags\" script.\n\n*Release 20240201*:\nFSTags.attach_path: do not save the tagfile if the FSTags is open - the final close will do it.\n\n*Release 20231129*:\nFSTags.startup_shutdown: run self.sync() in a finally clause.\n\n*Release 20230407*:\n* FSTags: support open/close of self.update_mapping, update startup/shutdown to startup_shutdown.\n* Move the (optional) ORM open/close from FSTags.startup_shutdown to TagFile.save, greatly shortens the ORM lock.\n\n*Release 20230217*:\nFSTagsCommand.cmd_rename: use -o for the format string as for other commands, -n is for \"no action\".\n\n*Release 20230212*:\n* FSTags.keypath(fspath) and TaggedPath.keypath with the singleton filesystem path for a TaggedPath.\n* TaggedPath.parent property.\n* TaggedPath.findup(check) method.\n* FSTagsCommand: cmd_find,cmd_ls: abort if runstate.cancelled thus honouring SIGINT/^C.\n\n*Release 20230211*:\nFSTags.__init__: treat empty $FSTAGS_UPDATE_MAPPING as missing i.e. None.\n\n*Release 20230210*:\nNew optional update_mapping for mirroring tags eg to an SQLTags; activated automatically by an $FSTAGS_UPDATE_MAPPING environment variable.\n\n*Release 20221228*:\nTaggedPath.save: new options prune=True parameter to drop empty top level dict/lists.\n\n*Release 20220918*:\n* FSTagsConfig: add empty .provided dict for config overrides.\n* FSTagsConfig: set FSPATH_DEFAULT=RCFILE for use by FSPathBasedSingleton._singleton_key.\n* FSTagsConfig: accept optional `physical` parameter to choose `realpath` over `abspath` and new `.physical` property.\n* FSTags.__getitem__: use `abspath` or `realpath` depending on `self.config.physical`.\n* FSTagsCommand: new -P option for \"physical\" mode, plumb though to the config and self.options.\n* Provide a DEFAULT_FSTAGS instance and an @uses_fstags decorator.\n\n*Release 20220606*:\nFSTagsCommand.edit: mention the filename in the header comments in tagset edit mode.\n\n*Release 20220430*:\nMinor bugfixes.\n\n*Release 20220311*:\n* New TaggedPath.infer_tags() method to compute and return the inferred TagSet.\n* New \"fstags infer pathname\" command.\n\n*Release 20211212*:\n* Rename edit_many to edit_tagsets for clarity.\n* FSTags.edit_dirpath: include realpath(dirpath) at the top of the edit list.\n\n*Release 20210906*:\n* TaggedPath: new auto_infer method overriding the inherited TagSet.auto_infer which consults the cascade_rules from the .fstagsrc.\n* fstags ls: new -l option to print a multiline tag listing.\n* fstags cptags: copy the tags from one path to another.\n* Assorted other changes.\n\n*Release 20210404*:\n* FSTags.edit_dirpath: new all_names parameter to include dot-names.\n* FSTagsCommand.cmd_edit: new -a option to turn on the all_names parameter.\n\n*Release 20210306*:\n* FSTags: new tagfile_for(filepath) to obtain the TagFile for filepath.\n* FSTagsCommand: new -o ontology option to supply an ontology file for the FSTags.\n* FSTagsCommand.cmd_ont: drop -o/--ontology, superceded by global -o option.\n* Move BaseTagFile from cs.fstags to TagFile in cs.tagset.\n* FSTags.edit_dirpath: we now get (old_name,new_name,TaggedPath) back from edit_many, obviating the te_id_map.\n* TaggedPath: .name property returning basename(self.filepath), .set and .discard methods rejecting use of the 'name' tag.\n* FSTagsCommand: port to new cs.cmdutils API.\n* Many small refactors and bugfixes.\n\n*Release 20200717.1*:\nAdd the manual page MarkDown source files. Note: well out of date, need updating.\n\n*Release 20200717*:\nDISTINFO: require cs.obj>=20200716 for SingletonMixin API change.\n\n*Release 20200716*:\n* Update for changed cs.obj.SingletonMixin API.\n* fstags: new \"export\" and \"import\" subcommands roughly paralleling those from \"sqltags\", providing CSV formatted export/import.\n* fstags export: ew -a (all paths) option - the default is not to not export paths with no tags.\n\n*Release 20200521.1*:\nfix DISTINFO.install_requires\n\n*Release 20200521*:\n* Add -i option to cp,ln,mv for command line compatibility, just disables -f.\n* New \"rename\" subcommand to rename files according to a format string.\n* Ontology support (optional).\n* Various classes are now singletons to avoid dissonance.\n* Fold \"edittags\" subcommand into \"edit\" via the \"-d\" (directories like files) option.\n* New \"ns\" subcommand reporting on the primary names available for formatting.\n* Accept [clausename]entryname as a format string to obtain the string from that entry of the config file.\n* Many bugfixes and improvements.\n\n*Release 20200229*:\n* New TaggedPath.modified property aliasing the TagSet.modified attribute.\n* ls: new -d option to treat directories like files (do not recurse), aiding reporting of tags for a directory.\n* find,ls subcommands: work off the realpath of the supplied top level path.\n* Tag: now subclasses namedtuple.\n* Rewrite rpaths() to use scandir and to also yield (is_dir,path) tuples.\n* TagSet, Tag, TagChoice moved into new cs.tagset module for reuse.\n* json_import: make --prefix mandatory, is \".\" as separator if not empty.\n* Move filename regexp rules to [filename_rules] config section.\n* New CascadeRule for representing a \"target_tag_name = tag_name1 tag_name2...\" config rules.\n* autotag: include the cascade rules in the autotagging after the filename rules.\n\n*Release 20200210*:\n* New \"json_import\" subcommand to import a JSON dict as tags, initial use case to load the metadata from youtube-dl.\n* New \"scrub\" command line operation, to purge tags of paths which do not exist.\n* New \"cp\", \"ln\" and \"mv\" subcommands to copy/link/move paths and take their tags with them.\n* New \"test\" subcommand to test paths against tag criteria, useful for find and scripts.\n* Small bugfixes.\n\n*Release 20200130*:\n* New FSTagsConfig class which parses the .fstagsrc as a .ini file; related adjustments.\n* New HasFSTagsMixin presenting a settable .fstags property with a shared default.\n* New xattr_import and xattr_export subcommands, remove implicit xattr access/update from other operations.\n* New TagSet.__len__ returning the number of tags.\n* Add \"-\" support for stdin to \"tag\" and \"tagpaths\" subcommands.\n\n*Release 20200113.2*:\nFSTagsCommand docstring tweak.\n\n*Release 20200113.1*:\nSmall docstring updates.\n\n*Release 20200113*:\nMirror tags to user.cs.fstags xattr to honour Linux namespace requirements. Add \"filesize\" to available tag string format (-o option). Small bugfixes.\n\n*Release 20191230*:\n* Command line: new \"find\" command to search a file tree based on tags.\n* Command line: new \"mv\" command to move a file and its tags.\n* Command line: Python string formats for \"find\" and \"ls\" output.\n* TaggedPath.autotag: new optional `no_save` parameter, default False, to suppress update of the associated .fstags file.\n* Inital and untested \"mirror tags to xattrs\" support.\n\n*Release 20191201*:\nNew \"edit\" subcommand to rename files and edit tags.\n\n*Release 20191130.1*:\nInitial release: fstags, filesystem based tagging utility.\n\n",
    "bugtrack_url": null,
    "license": "GNU General Public License v3 or later (GPLv3+)",
    "summary": "Simple filesystem based file tagging and the associated `fstags` command line script.",
    "version": "20240422",
    "project_urls": {
        "URL": "https://bitbucket.org/cameron_simpson/css/commits/all"
    },
    "split_keywords": [
        "python3"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "56c2ab503c4b950e8533d2437bc7ec1df75d823809a0898527275a7c840c34d1",
                "md5": "9a209032eadac91d039a503ce5583022",
                "sha256": "f428e3b3dd18cb818ff0f71c2da729b66a3f6795983598c1f2cae43afcce3e91"
            },
            "downloads": -1,
            "filename": "cs.fstags-20240422-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "9a209032eadac91d039a503ce5583022",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": null,
            "size": 29229,
            "upload_time": "2024-04-22T03:03:40",
            "upload_time_iso_8601": "2024-04-22T03:03:40.718619Z",
            "url": "https://files.pythonhosted.org/packages/56/c2/ab503c4b950e8533d2437bc7ec1df75d823809a0898527275a7c840c34d1/cs.fstags-20240422-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "c2ce2d7791f172f5bcd4f4dd12821ba2cd84502e88f3f023ae249636b5aee0d0",
                "md5": "1b82934e486331d4c01b4a5d48a6f086",
                "sha256": "35e0399b07e75ecf0d0b5c38c7bfc387507c406cb674741e5dc0bf5226fa7a18"
            },
            "downloads": -1,
            "filename": "cs.fstags-20240422.tar.gz",
            "has_sig": false,
            "md5_digest": "1b82934e486331d4c01b4a5d48a6f086",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 65111,
            "upload_time": "2024-04-22T03:03:42",
            "upload_time_iso_8601": "2024-04-22T03:03:42.896689Z",
            "url": "https://files.pythonhosted.org/packages/c2/ce/2d7791f172f5bcd4f4dd12821ba2cd84502e88f3f023ae249636b5aee0d0/cs.fstags-20240422.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-04-22 03:03:42",
    "github": false,
    "gitlab": false,
    "bitbucket": true,
    "codeberg": false,
    "bitbucket_user": "cameron_simpson",
    "bitbucket_project": "css",
    "lcname": "cs.fstags"
}
        
Elapsed time: 0.23215s