#!/usr/bin/env python
from setuptools import setup
setup(
  name = 'cs.fstags',
  author = 'Cameron Simpson',
  author_email = 'cs@cskk.id.au',
  version = '20200521.1',
  url = 'https://bitbucket.org/cameron_simpson/css/commits/all',
  description =
    ('Simple filesystem based file tagging and the associated `fstags` command '    
 'line script.'),
  long_description =
    ('Simple filesystem based file tagging\n'    
 'and the associated `fstags` command line script.\n'    
 '\n'    
 '*Latest release 20200521.1*:\n'    
 'fix DISTINFO.install_requires\n'    
 '\n'    
 'Many basic tasks can be performed with the `fstags` command line utility,\n'    
 'documented under the `FSTagsCommand` class below.\n'    
 '\n'    
 'Why `fstags`?\n'    
 'By 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'    
 '\n'    
 'Tags are stored in the file `.fstags` in each directory;\n'    
 'there is a line for each entry in the directory with tags\n'    
 'consisting of the directory entry name and the associated tags.\n'    
 '\n'    
 'Tags may be "bare", or have a value.\n'    
 "If there is a value it is expressed with an equals (`'='`)\n"    
 'followed by the JSON encoding of the value.\n'    
 '\n'    
 'The tags for a file are the union of its direct tags\n'    
 'and all relevant ancestor tags,\n'    
 'with priority given to tags closer to the file.\n'    
 '\n'    
 'For example, a media file for a television episode with the pathname\n'    
 '`/path/to/series-name/season-02/episode-name--s02e03--something.mp4`\n'    
 'might 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'    
 '\n'    
 'obtained from the following `.fstags` entries:\n'    
 '* tag file `/path/to/.fstags`:\n'    
 '  `series-name sf series_title="Series Full Name"`\n'    
 '* tag file `/path/to/series-name/.fstags`:\n'    
 '  `season-02 season=2`\n'    
 '* tag file `/path/to/series-name/season-02/.fstags`:\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'    
 '\n'    
 'Walk the media tree for files tagged for backup to `archive2`:\n'    
 '\n'    
 '    find /path/to/media backup=archive2\n'    
 '\n'    
 'Walk the media tree for files not assigned to a backup archive:\n'    
 '\n'    
 '    find /path/to/media -backup\n'    
 '\n'    
 'Backup the `archive2` files using `rsync`:\n'    
 '\n'    
 '    fstags find --for-rsync /path/to/media backup=archive2         | rsync '    
 '-ia --include-from=- /path/to/media /path/to/backup_archive2\n'    
 '\n'    
 '## Class `CascadeRule`\n'    
 '\n'    
 'A cascade rule of possible source tag names to provide a target tag.\n'    
 '\n'    
 '## Class `FSTags(cs.resources.MultiOpenMixin)`\n'    
 '\n'    
 'A class to examine filesystem tags.\n'    
 '\n'    
 '## Class '    
 '`FSTagsCommand(cs.cmdutils.BaseCommand,cs.tagset.TagsCommandMixin)`\n'    
 '\n'    
 '`fstags` main command line utility.\n'    
 '\n'    
 '\n'    
 'Command line usage:\n'    
 '\n'    
 '    Usage: FSTagsCommand subcommand [...]\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'    
 '        edit [-d] [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'    
 '          -d          Treat directories like files: edit just its 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: {filepath.pathname}\n'    
 '        help [subcommand-names...]\n'    
 '          Print the help for the named subcommands,\n'    
 '          or for all subcommands if no names are specified.\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 [-d] [--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'    
 '          -o output_format\n'    
 '                      Use output_format as a Python format string to lay '    
 'out\n'    
 '                      the listing.\n'    
 '                      Default: {filepath.encoded} {tags}\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, locate the ontology.\n'    
 '          Subcommands:\n'    
 '            tags tag[=value]...\n'    
 '              Query ontology information for the specified tags.\n'    
 '        rename -n newbasename_format paths...\n'    
 '          Rename paths according to a format string.\n'    
 '          -n newbasename_format\n'    
 '              Use newbasename_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'    
 '        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'    
 '## Class `FSTagsConfig`\n'    
 '\n'    
 'A configuration for fstags.\n'    
 '\n'    
 '### Method `FSTagsConfig.__init__(self, rcfilepath=None)`\n'    
 '\n'    
 'Initialise the config.\n'    
 '\n'    
 'Parameters:\n'    
 '* `rcfilepath`: the path to the confguration file\n'    
 "  If `None`, default to `'~/.fstagsrc'` (from `RCFILE`).\n"    
 '\n'    
 '## Function `get_xattr_value(filepath, xattr_name)`\n'    
 '\n'    
 'Read the extended attribute `xattr_name` of `filepath`.\n'    
 'Return the extended attribute value as a string,\n'    
 'or `None` if the attribute does not exist.\n'    
 '\n'    
 'Parameters:\n'    
 '* `filepath`: 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'    
 '\n'    
 'Mixin providing a `.fstags` property.\n'    
 '\n'    
 '## Function `main(argv=None)`\n'    
 '\n'    
 'Command line mode.\n'    
 '\n'    
 '## Class `RegexpTagRule`\n'    
 '\n'    
 'A regular expression based `Tag` rule.\n'    
 '\n'    
 '## Function `rfilepaths(path, name_selector=None, U=None)`\n'    
 '\n'    
 'Generator yielding pathnames of files found under `path`.\n'    
 '\n'    
 '## Function `rpaths(path, *, yield_dirs=False, name_selector=None, U=None)`\n'    
 '\n'    
 'Recurse over `path`, yielding `(is_dir,subpath)`\n'    
 'for all selected subpaths.\n'    
 '\n'    
 '## Function `rsync_patterns(paths, top_path)`\n'    
 '\n'    
 'Return a list of rsync include lines\n'    
 'suitable for use with the `--include-from` option.\n'    
 '\n'    
 '## Class `TagFile(cs.obj.SingletonMixin)`\n'    
 '\n'    
 'A reference to a specific file containing tags.\n'    
 '\n'    
 'This manages a mapping of `name` => `TagSet`,\n'    
 'itself a mapping of tag name => tag value.\n'    
 '\n'    
 '## Class `TagFileEntry(TagFileEntry,builtins.tuple)`\n'    
 '\n'    
 'An entry withing a `TagFile`.\n'    
 '\n'    
 'Attributes:\n'    
 '* `name`: the name of the `TagSet` entry within `tagfile`\n'    
 '* `tagfile`: the `TagFile` containing `name`\n'    
 '\n'    
 '## Class `TaggedPath(HasFSTagsMixin,cs.lex.FormatableMixin)`\n'    
 '\n'    
 'Class to manipulate the tags for a specific path.\n'    
 '\n'    
 '## Function `update_xattr_value(filepath, xattr_name, new_xattr_value)`\n'    
 '\n'    
 'Update the extended attributes of `filepath`\n'    
 'with `new_xattr_value` for `xattr_name`.\n'    
 'Return the previous value, or `None` if the attribute was missing.\n'    
 '\n'    
 'We avoid calling `os.setxattr` if the value will not change.\n'    
 '\n'    
 'Parameters:\n'    
 '* `filepath`: 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'    
 '\n'    
 'Emit message if in verbose mode.\n'    
 '\n'    
 '# Release Log\n'    
 '\n'    
 '\n'    
 '\n'    
 '*Release 20200521.1*:\n'    
 'fix 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*:\n'    
 'FSTagsCommand docstring tweak.\n'    
 '\n'    
 '*Release 20200113.1*:\n'    
 'Small docstring updates.\n'    
 '\n'    
 '*Release 20200113*:\n'    
 'Mirror 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*:\n'    
 'New "edit" subcommand to rename files and edit tags.\n'    
 '\n'    
 '*Release 20191130.1*:\n'    
 'Initial release: fstags, filesystem based tagging utility.'),
  classifiers = ['Programming Language :: Python', 'Programming Language :: Python :: 3', 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'Operating System :: OS Independent', 'Topic :: Software Development :: Libraries :: Python Modules', 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)'],
  entry_points = {'console_scripts': ['fstags = cs.fstags:main']},
  install_requires = ['cs.cmdutils', 'cs.context', 'cs.deco', 'cs.edit', 'cs.fileutils', 'cs.lex', 'cs.logutils', 'cs.obj', 'cs.pfx', 'cs.resources', 'cs.tagset', 'cs.threads', 'cs.upd', 'icontract'],
  keywords = ['python3'],
  license = 'GNU General Public License v3 or later (GPLv3+)',
  long_description_content_type = 'text/markdown',
  package_dir = {'': 'lib/python'},
  py_modules = ['cs.fstags'],
)
