diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..55bfb6d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,56 @@ +# Generated from: +# https://github.com/plone/meta/tree/2.x/src/plone/meta/default +# See the inline comments on how to expand/tweak this configuration file +# +# EditorConfig Configuration file, for more details see: +# http://EditorConfig.org +# EditorConfig is a convention description, that could be interpreted +# by multiple editors to enforce common coding conventions for specific +# file types + +# top-most EditorConfig file: +# Will ignore other EditorConfig files in Home directory or upper tree level. +root = true + + +[*] +# Default settings for all files. +# Unix-style newlines with a newline ending every file +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +# Set default charset +charset = utf-8 +# Indent style default +indent_style = space +# Max Line Length - a hard line wrap, should be disabled +max_line_length = off + +[*.{py,cfg,ini}] +# 4 space indentation +indent_size = 4 + +[*.{yml,zpt,pt,dtml,zcml,html,xml}] +# 2 space indentation +indent_size = 2 + +[*.{json,jsonl,js,jsx,ts,tsx,css,less,scss}] +# Frontend development +# 2 space indentation +indent_size = 2 +max_line_length = 80 + +[{Makefile,.gitmodules}] +# Tab indentation (no size specified, but view as 4 spaces) +indent_style = tab +indent_size = unset +tab_width = unset + + +## +# Add extra configuration options in .meta.toml: +# [editorconfig] +# extra_lines = """ +# _your own configuration lines_ +# """ +## diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..972bb41 --- /dev/null +++ b/.flake8 @@ -0,0 +1,11 @@ +[flake8] +doctests = 1 +ignore = + # black takes care of line length + E501, + # black takes care of where to break lines + W503, + # black takes care of spaces within slicing (list[:]) + E203, + # black takes care of spaces after commas + E231, diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8e165f1..c63ee45 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,6 +13,15 @@ on: workflow_dispatch: jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7.0.1 + with: + persist-credentials: false + - name: Run code quality checks + run: pipx run pre-commit run -a + test: runs-on: ubuntu-latest strategy: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..76f49c2 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,35 @@ +ci: + autofix_prs: false + autoupdate_schedule: monthly + +repos: +- repo: https://github.com/asottile/pyupgrade + rev: v3.21.2 + hooks: + - id: pyupgrade + args: [--py310-plus] +- repo: https://github.com/pycqa/isort + rev: 9.0.0b1 + hooks: + - id: isort +- repo: https://github.com/psf/black-pre-commit-mirror + rev: 26.5.1 + hooks: + - id: black +- repo: https://github.com/PyCQA/flake8 + rev: 7.3.0 + hooks: + - id: flake8 +- repo: https://github.com/mgedmin/check-manifest + rev: "0.51" + hooks: + - id: check-manifest +- repo: https://github.com/regebro/pyroma + rev: "5.1b1" + hooks: + - id: pyroma +- repo: https://github.com/mgedmin/check-python-versions + rev: "0.24.2" + hooks: + - id: check-python-versions + args: ['--only', 'setup.py,tox.ini'] diff --git a/MANIFEST.in b/MANIFEST.in index c80f692..bb37a27 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1 @@ include *.rst -include *.ini diff --git a/pyproject.toml b/pyproject.toml index ea9e0d7..a68e1c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,8 +1,19 @@ [build-system] -# We still support older Pythons, and that means it is better to use an -# older setuptools to create the distributions. -# requires = ["setuptools<69"] -# But with such an old version, I get a BadRequest error when uploading to PyPI: -# Filename 'mr.developer-2.1.0.tar.gz' is invalid, should be 'mr_developer-2.1.0.tar.gz'. -# So require a newer one. requires = ["setuptools>=75.3.2"] +build-backend = "setuptools.build_meta" + +[tool.black] +target-version = ["py310"] + +[tool.check-manifest] +ignore = [ + ".editorconfig", + ".flake8", + ".pre-commit-config.yaml", + "tox.ini", + "build_git.sh", + "buildout.cfg", +] + +[tool.isort] +profile = "plone" diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index a49c593..0000000 --- a/setup.cfg +++ /dev/null @@ -1,13 +0,0 @@ -[zest.releaser] -version-levels = 2 - -[devpi:upload] -formats = sdist.tgz,bdist_wheel - -[flake8] -extend-ignore = E501 - -[check-manifest] -ignore = - build_git.sh - buildout.cfg diff --git a/setup.py b/setup.py index cf4be76..4fd426a 100644 --- a/setup.py +++ b/setup.py @@ -1,62 +1,64 @@ from setuptools import setup - -version = '3.0.0.dev0' +version = "3.0.0.dev0" install_requires = [ - 'setuptools', - 'zc.buildout', + "setuptools", + "zc.buildout", ] -tests_require = [ - 'mock'] +tests_require = ["mock"] -extras_require = { - 'test': tests_require} +extras_require = {"test": tests_require} def get_text_from_file(fn): - text = open(fn, 'rb').read() - return text.decode('utf-8') + text = open(fn, "rb").read() + return text.decode("utf-8") -setup(name='mr.developer', - version=version, - description="A zc.buildout extension to ease the development of large projects with lots of packages.", - long_description="\n\n".join([ - get_text_from_file("README.rst"), - get_text_from_file("HELP.rst"), - get_text_from_file("CHANGES.rst")]), - # Get more strings from https://pypi.org/classifiers/ - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Programming Language :: Python :: 3.14", - "Programming Language :: Python :: 3.15", - "Framework :: Buildout", - "Topic :: Software Development :: Libraries :: Python Modules"], - keywords='buildout extension vcs git develop', - author='Florian Schulze', - author_email='florian.schulze@gmx.net', - url='https://github.com/fschulze/mr.developer', - license='BSD', - packages=['mr', 'mr.developer', 'mr.developer.tests'], - package_dir={'': 'src'}, - namespace_packages=['mr', 'mr.developer'], - include_package_data=True, - zip_safe=False, - install_requires=install_requires, - tests_require=tests_require, - extras_require=extras_require, - python_requires=">=3.10", - test_suite='mr.developer.tests', - entry_points=""" +setup( + name="mr.developer", + version=version, + description="A zc.buildout extension to ease the development of large projects with lots of packages.", + long_description="\n\n".join( + [ + get_text_from_file("README.rst"), + get_text_from_file("HELP.rst"), + get_text_from_file("CHANGES.rst"), + ] + ), + # Get more strings from https://pypi.org/classifiers/ + classifiers=[ + "Development Status :: 5 - Production/Stable", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.15", + "Framework :: Buildout", + "Topic :: Software Development :: Libraries :: Python Modules", + ], + keywords="buildout extension vcs git develop", + author="Florian Schulze", + author_email="florian.schulze@gmx.net", + url="https://github.com/fschulze/mr.developer", + license="BSD", + packages=["mr", "mr.developer", "mr.developer.tests"], + package_dir={"": "src"}, + namespace_packages=["mr", "mr.developer"], + include_package_data=True, + zip_safe=False, + install_requires=install_requires, + tests_require=tests_require, + extras_require=extras_require, + python_requires=">=3.10", + test_suite="mr.developer.tests", + entry_points=""" [console_scripts] develop = mr.developer.develop:develop [zc.buildout.extension] @@ -84,4 +86,5 @@ def get_text_from_file(fn): reset = mr.developer.commands:CmdReset status = mr.developer.commands:CmdStatus update = mr.developer.commands:CmdUpdate - """) + """, +) diff --git a/src/mr/__init__.py b/src/mr/__init__.py index f48ad10..05f0beb 100644 --- a/src/mr/__init__.py +++ b/src/mr/__init__.py @@ -1,6 +1,7 @@ # See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages try: - __import__('pkg_resources').declare_namespace(__name__) + __import__("pkg_resources").declare_namespace(__name__) except ImportError: from pkgutil import extend_path + __path__ = extend_path(__path__, __name__) diff --git a/src/mr/developer/__init__.py b/src/mr/developer/__init__.py index f48ad10..05f0beb 100644 --- a/src/mr/developer/__init__.py +++ b/src/mr/developer/__init__.py @@ -1,6 +1,7 @@ # See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages try: - __import__('pkg_resources').declare_namespace(__name__) + __import__("pkg_resources").declare_namespace(__name__) except ImportError: from pkgutil import extend_path + __path__ = extend_path(__path__, __name__) diff --git a/src/mr/developer/bazaar.py b/src/mr/developer/bazaar.py index a3af456..bbea7ae 100644 --- a/src/mr/developer/bazaar.py +++ b/src/mr/developer/bazaar.py @@ -1,4 +1,5 @@ from mr.developer import common + import os import subprocess @@ -13,98 +14,110 @@ class BazaarWorkingCopy(common.BaseWorkingCopy): def __init__(self, source): super().__init__(source) - self.bzr_executable = common.which('bzr') + self.bzr_executable = common.which("bzr") def bzr_branch(self, **kwargs): - name = self.source['name'] - path = self.source['path'] - url = self.source['url'] + name = self.source["name"] + path = self.source["path"] + url = self.source["url"] if os.path.exists(path): - self.output( - (logger.info, 'Skipped branching existing package %r.' % name)) + self.output((logger.info, "Skipped branching existing package %r." % name)) return - self.output((logger.info, 'Branched %r with bazaar.' % name)) + self.output((logger.info, "Branched %r with bazaar." % name)) env = dict(os.environ) - env.pop('PYTHONPATH', None) + env.pop("PYTHONPATH", None) cmd = subprocess.Popen( - [self.bzr_executable, 'branch', '--quiet', url, path], - env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + [self.bzr_executable, "branch", "--quiet", url, path], + env=env, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) stdout, stderr = cmd.communicate() if cmd.returncode != 0: - raise BazaarError( - f'bzr branch for {name!r} failed.\n{stderr}') - if kwargs.get('verbose', False): + raise BazaarError(f"bzr branch for {name!r} failed.\n{stderr}") + if kwargs.get("verbose", False): return stdout def bzr_pull(self, **kwargs): - name = self.source['name'] - path = self.source['path'] - url = self.source['url'] - self.output((logger.info, 'Updated %r with bazaar.' % name)) + name = self.source["name"] + path = self.source["path"] + url = self.source["url"] + self.output((logger.info, "Updated %r with bazaar." % name)) env = dict(os.environ) - env.pop('PYTHONPATH', None) + env.pop("PYTHONPATH", None) cmd = subprocess.Popen( - [self.bzr_executable, 'pull', url], cwd=path, - env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + [self.bzr_executable, "pull", url], + cwd=path, + env=env, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) stdout, stderr = cmd.communicate() if cmd.returncode != 0: - raise BazaarError( - f'bzr pull for {name!r} failed.\n{stderr}') - if kwargs.get('verbose', False): + raise BazaarError(f"bzr pull for {name!r} failed.\n{stderr}") + if kwargs.get("verbose", False): return stdout def checkout(self, **kwargs): - name = self.source['name'] - path = self.source['path'] + name = self.source["name"] + path = self.source["path"] update = self.should_update(**kwargs) if os.path.exists(path): if update: self.update(**kwargs) elif self.matches(): self.output( - (logger.info, 'Skipped checkout of existing package %r.' % name)) + (logger.info, "Skipped checkout of existing package %r." % name) + ) else: raise BazaarError( - 'Source URL for existing package %r differs. ' - 'Expected %r.' % (name, self.source['url'])) + "Source URL for existing package %r differs. " + "Expected %r." % (name, self.source["url"]) + ) else: return self.bzr_branch(**kwargs) def matches(self): - name = self.source['name'] - path = self.source['path'] + name = self.source["name"] + path = self.source["path"] env = dict(os.environ) - env.pop('PYTHONPATH', None) + env.pop("PYTHONPATH", None) cmd = subprocess.Popen( - [self.bzr_executable, 'info'], cwd=path, - env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + [self.bzr_executable, "info"], + cwd=path, + env=env, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) stdout, stderr = cmd.communicate() if cmd.returncode != 0: - raise BazaarError( - f'bzr info for {name!r} failed.\n{stderr}') - return (self.source['url'] in stdout.split()) + raise BazaarError(f"bzr info for {name!r} failed.\n{stderr}") + return self.source["url"] in stdout.split() def status(self, **kwargs): - path = self.source['path'] + path = self.source["path"] env = dict(os.environ) - env.pop('PYTHONPATH', None) + env.pop("PYTHONPATH", None) cmd = subprocess.Popen( - [self.bzr_executable, 'status'], cwd=path, - env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + [self.bzr_executable, "status"], + cwd=path, + env=env, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) stdout, stderr = cmd.communicate() - status = stdout and 'dirty' or 'clean' - if kwargs.get('verbose', False): + status = stdout and "dirty" or "clean" + if kwargs.get("verbose", False): return status, stdout else: return status def update(self, **kwargs): - name = self.source['name'] + name = self.source["name"] if not self.matches(): raise BazaarError( - "Can't update package %r because its URL doesn't match." % - name) - if self.status() != 'clean' and not kwargs.get('force', False): - raise BazaarError( - "Can't update package %r because it's dirty." % name) + "Can't update package %r because its URL doesn't match." % name + ) + if self.status() != "clean" and not kwargs.get("force", False): + raise BazaarError("Can't update package %r because it's dirty." % name) return self.bzr_pull(**kwargs) diff --git a/src/mr/developer/commands.py b/src/mr/developer/commands.py index 4b49448..7b729ca 100644 --- a/src/mr/developer/commands.py +++ b/src/mr/developer/commands.py @@ -1,4 +1,8 @@ -from mr.developer.common import logger, memoize, WorkingCopies, yesno +from mr.developer.common import logger +from mr.developer.common import memoize +from mr.developer.common import WorkingCopies +from mr.developer.common import yesno + import argparse import errno import os @@ -14,15 +18,19 @@ class ChoicesPseudoAction(argparse.Action): def __init__(self, *args, **kwargs): sup = super() - sup.__init__(dest=args[0], option_strings=list(args), help=kwargs.get('help'), nargs=0) + sup.__init__( + dest=args[0], option_strings=list(args), help=kwargs.get("help"), nargs=0 + ) class ArgumentParser(argparse.ArgumentParser): def _check_value(self, action, value): # converted value must be one of the choices (if specified) if action.choices is not None and value not in action.choices: - tup = value, ', '.join([repr(x) for x in sorted(action.choices) if x != 'pony']) - msg = argparse._('invalid choice: %r (choose from %s)') % tup + tup = value, ", ".join( + [repr(x) for x in sorted(action.choices) if x != "pony"] + ) + msg = argparse._("invalid choice: %r (choose from %s)") % tup raise argparse.ArgumentError(action, msg) @@ -46,8 +54,7 @@ def get_workingcopies(self, sources): return WorkingCopies(sources, threads=self.develop.threads) @memoize - def get_packages(self, args, auto_checkout=False, - develop=False, checked_out=False): + def get_packages(self, args, auto_checkout=False, develop=False, checked_out=False): if auto_checkout: packages = set(self.develop.auto_checkout) else: @@ -69,7 +76,9 @@ def get_packages(self, args, auto_checkout=False, if len(result) == 0: if len(args) > 1: - regexps = "{} or '{}'".format(", ".join("'%s'" % x for x in args[:-1]), args[-1]) + regexps = "{} or '{}'".format( + ", ".join("'%s'" % x for x in args[:-1]), args[-1] + ) else: regexps = "'%s'" % args[0] logger.error("No package matched %s." % regexps) @@ -83,48 +92,71 @@ def __init__(self, develop): Command.__init__(self, develop) description = "Add packages to the list of development packages." self.parser = self.develop.parsers.add_parser( - "activate", - description=description) - self.develop.parsers._name_parser_map["a"] = self.develop.parsers._name_parser_map["activate"] - self.develop.parsers._choices_actions.append(ChoicesPseudoAction( - "activate", "a", help=description)) - self.parser.add_argument( - "-a", "--auto-checkout", dest="auto_checkout", - action="store_true", default=False, - help="""Only considers packages declared by auto-checkout. If you don't specify a then all declared packages are processed.""") - self.parser.add_argument( - "-c", "--checked-out", dest="checked_out", - action="store_true", default=False, - help="""Only considers packages currently checked out. If you don't specify a then all checked out packages are processed.""") - self.parser.add_argument( - "-d", "--develop", dest="develop", - action="store_true", default=False, - help="""Only considers packages currently in development mode. If you don't specify a then all develop packages are processed.""") - self.parser.add_argument( - "package-regexp", nargs="+", - help="A regular expression to match package names.") + "activate", description=description + ) + self.develop.parsers._name_parser_map["a"] = ( + self.develop.parsers._name_parser_map["activate"] + ) + self.develop.parsers._choices_actions.append( + ChoicesPseudoAction("activate", "a", help=description) + ) + self.parser.add_argument( + "-a", + "--auto-checkout", + dest="auto_checkout", + action="store_true", + default=False, + help="""Only considers packages declared by auto-checkout. If you don't specify a then all declared packages are processed.""", + ) + self.parser.add_argument( + "-c", + "--checked-out", + dest="checked_out", + action="store_true", + default=False, + help="""Only considers packages currently checked out. If you don't specify a then all checked out packages are processed.""", + ) + self.parser.add_argument( + "-d", + "--develop", + dest="develop", + action="store_true", + default=False, + help="""Only considers packages currently in development mode. If you don't specify a then all develop packages are processed.""", + ) + self.parser.add_argument( + "package-regexp", + nargs="+", + help="A regular expression to match package names.", + ) self.parser.set_defaults(func=self) def __call__(self, args): config = self.develop.config - packages = self.get_packages(getattr(args, 'package-regexp'), - auto_checkout=args.auto_checkout, - checked_out=args.checked_out, - develop=args.develop) + packages = self.get_packages( + getattr(args, "package-regexp"), + auto_checkout=args.auto_checkout, + checked_out=args.checked_out, + develop=args.develop, + ) changed = False for name in sorted(packages): source = self.develop.sources[name] if not source.exists(): - logger.warning("The package '%s' matched, but isn't checked out." % name) + logger.warning( + "The package '%s' matched, but isn't checked out." % name + ) continue - if not source.get('egg', True): + if not source.get("egg", True): logger.warning("The package '%s' isn't an egg." % name) continue config.develop[name] = True logger.info("Activated '%s'." % name) changed = True if changed: - logger.warn("Don't forget to run buildout again, so the actived packages are actually used.") + logger.warn( + "Don't forget to run buildout again, so the actived packages are actually used." + ) config.save() @@ -133,11 +165,14 @@ def __init__(self, develop): Command.__init__(self, develop) description = "Print arguments used by last buildout which will be used with the 'rebuild' command." self.parser = self.develop.parsers.add_parser( - "arguments", - description=description) - self.develop.parsers._name_parser_map["args"] = self.develop.parsers._name_parser_map["arguments"] - self.develop.parsers._choices_actions.append(ChoicesPseudoAction( - "arguments", "args", help=description)) + "arguments", description=description + ) + self.develop.parsers._name_parser_map["args"] = ( + self.develop.parsers._name_parser_map["arguments"] + ) + self.develop.parsers._choices_actions.append( + ChoicesPseudoAction("arguments", "args", help=description) + ) self.parser.set_defaults(func=self) def __call__(self, args): @@ -150,40 +185,59 @@ def __init__(self, develop): Command.__init__(self, develop) self.parser = self.develop.parsers.add_parser( "checkout", - description="Make a checkout of the packages matching the regular expressions and add them to the list of development packages.") - self.develop.parsers._name_parser_map["co"] = self.develop.parsers._name_parser_map["checkout"] - self.develop.parsers._choices_actions.append(ChoicesPseudoAction( - "checkout", "co", help="Checkout packages")) + description="Make a checkout of the packages matching the regular expressions and add them to the list of development packages.", + ) + self.develop.parsers._name_parser_map["co"] = ( + self.develop.parsers._name_parser_map["checkout"] + ) + self.develop.parsers._choices_actions.append( + ChoicesPseudoAction("checkout", "co", help="Checkout packages") + ) self.parser.add_argument( - "-a", "--auto-checkout", dest="auto_checkout", - action="store_true", default=False, - help="""Only considers packages declared by auto-checkout. If you don't specify a then all declared packages are processed.""") + "-a", + "--auto-checkout", + dest="auto_checkout", + action="store_true", + default=False, + help="""Only considers packages declared by auto-checkout. If you don't specify a then all declared packages are processed.""", + ) self.parser.add_argument( - "-v", "--verbose", dest="verbose", - action="store_true", default=False, - help="""Show output of VCS command.""") + "-v", + "--verbose", + dest="verbose", + action="store_true", + default=False, + help="""Show output of VCS command.""", + ) self.parser.add_argument( - "package-regexp", nargs="+", - help="A regular expression to match package names.") + "package-regexp", + nargs="+", + help="A regular expression to match package names.", + ) self.parser.set_defaults(func=self) def __call__(self, args): config = self.develop.config - packages = self.get_packages(getattr(args, 'package-regexp'), - auto_checkout=args.auto_checkout) + packages = self.get_packages( + getattr(args, "package-regexp"), auto_checkout=args.auto_checkout + ) try: workingcopies = self.get_workingcopies(self.develop.sources) - workingcopies.checkout(sorted(packages), - verbose=args.verbose, - submodules=self.develop.update_git_submodules, - always_accept_server_certificate=self.develop.always_accept_server_certificate) + workingcopies.checkout( + sorted(packages), + verbose=args.verbose, + submodules=self.develop.update_git_submodules, + always_accept_server_certificate=self.develop.always_accept_server_certificate, + ) for name in sorted(packages): source = self.develop.sources[name] - if not source.get('egg', True): + if not source.get("egg", True): continue config.develop[name] = True logger.info("Activated '%s'." % name) - logger.warning("Don't forget to run buildout again, so the checked out packages are used as develop eggs.") + logger.warning( + "Don't forget to run buildout again, so the checked out packages are used as develop eggs." + ) config.save() except (ValueError, KeyError): logger.error(sys.exc_info()[1]) @@ -195,41 +249,62 @@ def __init__(self, develop): Command.__init__(self, develop) description = "Remove packages from the list of development packages." self.parser = self.develop.parsers.add_parser( - "deactivate", - description=description) - self.develop.parsers._name_parser_map["d"] = self.develop.parsers._name_parser_map["deactivate"] - self.develop.parsers._choices_actions.append(ChoicesPseudoAction( - "deactivate", "d", help=description)) - self.parser.add_argument( - "-a", "--auto-checkout", dest="auto_checkout", - action="store_true", default=False, - help="""Only considers packages declared by auto-checkout. If you don't specify a then all declared packages are processed.""") - self.parser.add_argument( - "-c", "--checked-out", dest="checked_out", - action="store_true", default=False, - help="""Only considers packages currently checked out. If you don't specify a then all checked out packages are processed.""") - self.parser.add_argument( - "-d", "--develop", dest="develop", - action="store_true", default=False, - help="""Only considers packages currently in development mode. If you don't specify a then all develop packages are processed.""") - self.parser.add_argument( - "package-regexp", nargs="+", - help="A regular expression to match package names.") + "deactivate", description=description + ) + self.develop.parsers._name_parser_map["d"] = ( + self.develop.parsers._name_parser_map["deactivate"] + ) + self.develop.parsers._choices_actions.append( + ChoicesPseudoAction("deactivate", "d", help=description) + ) + self.parser.add_argument( + "-a", + "--auto-checkout", + dest="auto_checkout", + action="store_true", + default=False, + help="""Only considers packages declared by auto-checkout. If you don't specify a then all declared packages are processed.""", + ) + self.parser.add_argument( + "-c", + "--checked-out", + dest="checked_out", + action="store_true", + default=False, + help="""Only considers packages currently checked out. If you don't specify a then all checked out packages are processed.""", + ) + self.parser.add_argument( + "-d", + "--develop", + dest="develop", + action="store_true", + default=False, + help="""Only considers packages currently in development mode. If you don't specify a then all develop packages are processed.""", + ) + self.parser.add_argument( + "package-regexp", + nargs="+", + help="A regular expression to match package names.", + ) self.parser.set_defaults(func=self) def __call__(self, args): config = self.develop.config - packages = self.get_packages(getattr(args, 'package-regexp'), - auto_checkout=args.auto_checkout, - checked_out=args.checked_out, - develop=args.develop) + packages = self.get_packages( + getattr(args, "package-regexp"), + auto_checkout=args.auto_checkout, + checked_out=args.checked_out, + develop=args.develop, + ) changed = False for name in sorted(packages): source = self.develop.sources[name] if not source.exists(): - logger.warning("The package '%s' matched, but isn't checked out." % name) + logger.warning( + "The package '%s' matched, but isn't checked out." % name + ) continue - if not source.get('egg', True): + if not source.get("egg", True): logger.warning("The package '%s' isn't an egg." % name) continue if config.develop.get(name) is not False: @@ -237,7 +312,9 @@ def __call__(self, args): logger.info("Deactivated '%s'." % name) changed = True if changed: - logger.warn("Don't forget to run buildout again, so the deactived packages are actually not used anymore.") + logger.warn( + "Don't forget to run buildout again, so the deactived packages are actually not used anymore." + ) config.save() @@ -246,34 +323,42 @@ def __init__(self, develop): Command.__init__(self, develop) self.parser = self.develop.parsers.add_parser( "help", - description="Show help on the given command or about the whole script if none given.") - self.develop.parsers._name_parser_map["h"] = self.develop.parsers._name_parser_map["help"] - self.develop.parsers._choices_actions.append(ChoicesPseudoAction( - "help", "h", help="Show help")) - self.parser.add_argument( - "--rst", dest="rst", - action="store_true", default=False, - help="""Print help for all commands in reStructuredText format.""") - self.parser.add_argument( - '-z', '--zsh', - action='store_true', - help="Print info for zsh autocompletion") - self.parser.add_argument("command", nargs="?", help="The command you want to see the help of.") + description="Show help on the given command or about the whole script if none given.", + ) + self.develop.parsers._name_parser_map["h"] = ( + self.develop.parsers._name_parser_map["help"] + ) + self.develop.parsers._choices_actions.append( + ChoicesPseudoAction("help", "h", help="Show help") + ) + self.parser.add_argument( + "--rst", + dest="rst", + action="store_true", + default=False, + help="""Print help for all commands in reStructuredText format.""", + ) + self.parser.add_argument( + "-z", "--zsh", action="store_true", help="Print info for zsh autocompletion" + ) + self.parser.add_argument( + "command", nargs="?", help="The command you want to see the help of." + ) self.parser.set_defaults(func=self) def __call__(self, args): develop = self.develop choices = develop.parsers.choices if args.zsh: - choices = [x for x in choices if x != 'pony'] + choices = [x for x in choices if x != "pony"] if args.command is None: print("\n".join(choices)) else: - if args.command == 'help': + if args.command == "help": print("\n".join(choices)) - elif args.command in ('purge', 'up', 'update'): + elif args.command in ("purge", "up", "update"): print("\n".join(self.get_packages(None, checked_out=True))) - elif args.command not in ('pony', 'rebuild'): + elif args.command not in ("pony", "rebuild"): print("\n".join(self.get_packages(None))) return if args.command in choices: @@ -281,7 +366,7 @@ def __call__(self, args): return cmds = {} for name in choices: - if name == 'pony': + if name == "pony": continue cmds.setdefault(choices[name], set()).add(name) for cmd, names in list(cmds.items()): @@ -299,8 +384,8 @@ def __call__(self, args): print() for name in sorted(cmds): cmd = cmds[name] - if len(cmd['aliases']): - header = "{} ({})".format(name, ", ".join(cmd['aliases'])) + if len(cmd["aliases"]): + header = "{} ({})".format(name, ", ".join(cmd["aliases"])) else: header = name print(header) @@ -308,7 +393,7 @@ def __call__(self, args): print() print("::") print() - for line in cmd['cmd'].format_help().split('\n'): + for line in cmd["cmd"].format_help().split("\n"): print(" %s" % line) print() else: @@ -316,8 +401,8 @@ def __call__(self, args): print("Available commands:") for name in sorted(cmds): cmd = cmds[name] - if len(cmd['aliases']): - print(" {} ({})".format(name, ", ".join(cmd['aliases']))) + if len(cmd["aliases"]): + print(" {} ({})".format(name, ", ".join(cmd["aliases"]))) else: print(" %s" % name) @@ -327,70 +412,99 @@ def __init__(self, develop): Command.__init__(self, develop) description = "Lists informations about packages." self.parser = self.develop.parsers.add_parser( - "info", - help=description, - description=description) + "info", help=description, description=description + ) self.parser.add_argument( - "-a", "--auto-checkout", dest="auto_checkout", - action="store_true", default=False, - help="""Only considers packages declared by auto-checkout. If you don't specify a then all declared packages are processed.""") + "-a", + "--auto-checkout", + dest="auto_checkout", + action="store_true", + default=False, + help="""Only considers packages declared by auto-checkout. If you don't specify a then all declared packages are processed.""", + ) self.parser.add_argument( - "-c", "--checked-out", dest="checked_out", - action="store_true", default=False, - help="""Only considers packages currently checked out. If you don't specify a then all declared packages are processed.""") + "-c", + "--checked-out", + dest="checked_out", + action="store_true", + default=False, + help="""Only considers packages currently checked out. If you don't specify a then all declared packages are processed.""", + ) self.parser.add_argument( - "-d", "--develop", dest="develop", - action="store_true", default=False, - help="""Only considers packages currently in development mode. If you don't specify a then all declared packages are processed.""") + "-d", + "--develop", + dest="develop", + action="store_true", + default=False, + help="""Only considers packages currently in development mode. If you don't specify a then all declared packages are processed.""", + ) info_opts = self.parser.add_argument_group( "Output options", - """The following options are used to print just the info you want, the order they are specified reflects the order in which the information will be printed.""") + """The following options are used to print just the info you want, the order they are specified reflects the order in which the information will be printed.""", + ) info_opts.add_argument( - "--name", dest="info", - action="append_const", const="name", - help="""Prints the name of the package.""") + "--name", + dest="info", + action="append_const", + const="name", + help="""Prints the name of the package.""", + ) info_opts.add_argument( - "-p", "--path", dest="info", - action="append_const", const="path", - help="""Prints the absolute path of the package.""") + "-p", + "--path", + dest="info", + action="append_const", + const="path", + help="""Prints the absolute path of the package.""", + ) info_opts.add_argument( - "--type", dest="info", - action="append_const", const="type", - help="""Prints the repository type of the package.""") + "--type", + dest="info", + action="append_const", + const="type", + help="""Prints the repository type of the package.""", + ) info_opts.add_argument( - "--url", dest="info", - action="append_const", const="url", - help="""Prints the URL of the package.""") + "--url", + dest="info", + action="append_const", + const="url", + help="""Prints the URL of the package.""", + ) self.parser.add_argument_group(info_opts) self.parser.add_argument( - "package-regexp", nargs="*", - help="A regular expression to match package names.") + "package-regexp", + nargs="*", + help="A regular expression to match package names.", + ) self.parser.set_defaults(func=self) def __call__(self, args): - packages = self.get_packages(getattr(args, 'package-regexp'), - auto_checkout=args.auto_checkout, - develop=args.develop, - checked_out=args.checked_out) + packages = self.get_packages( + getattr(args, "package-regexp"), + auto_checkout=args.auto_checkout, + develop=args.develop, + checked_out=args.checked_out, + ) for name in sorted(packages): source = self.develop.sources[name] if args.info: info = [] for key in args.info: - if key == 'name': + if key == "name": info.append(name) - elif key == 'path': - info.append(source['path']) - elif key == 'type': - info.append(source['kind']) - elif key == 'url': - info.append(source['url']) + elif key == "path": + info.append(source["path"]) + elif key == "type": + info.append(source["kind"]) + elif key == "url": + info.append(source["url"]) print(" ".join(info)) else: print("Name: %s" % name) - print("Path: %s" % source['path']) - print("Type: %s" % source['kind']) - print("URL: %s" % source['url']) + print("Path: %s" % source["path"]) + print("Type: %s" % source["kind"]) + print("URL: %s" % source["url"]) print() @@ -399,31 +513,52 @@ def __init__(self, develop): Command.__init__(self, develop) description = "Lists tracked packages." self.parser = self.develop.parsers.add_parser( - "list", - formatter_class=HelpFormatter, - description=description) - self.develop.parsers._name_parser_map["ls"] = self.develop.parsers._name_parser_map["list"] - self.develop.parsers._choices_actions.append(ChoicesPseudoAction( - "list", "ls", help=description)) + "list", formatter_class=HelpFormatter, description=description + ) + self.develop.parsers._name_parser_map["ls"] = ( + self.develop.parsers._name_parser_map["list"] + ) + self.develop.parsers._choices_actions.append( + ChoicesPseudoAction("list", "ls", help=description) + ) self.parser.add_argument( - "-a", "--auto-checkout", dest="auto_checkout", - action="store_true", default=False, - help="""Only show packages in auto-checkout list.""") + "-a", + "--auto-checkout", + dest="auto_checkout", + action="store_true", + default=False, + help="""Only show packages in auto-checkout list.""", + ) self.parser.add_argument( - "-c", "--checked-out", dest="checked_out", - action="store_true", default=False, - help="""Only considers packages currently checked out. If you don't specify a then all checked out packages are processed.""") + "-c", + "--checked-out", + dest="checked_out", + action="store_true", + default=False, + help="""Only considers packages currently checked out. If you don't specify a then all checked out packages are processed.""", + ) self.parser.add_argument( - "-d", "--develop", dest="develop", - action="store_true", default=False, - help="""Only considers packages currently in development mode. If you don't specify a then all develop packages are processed.""") + "-d", + "--develop", + dest="develop", + action="store_true", + default=False, + help="""Only considers packages currently in development mode. If you don't specify a then all develop packages are processed.""", + ) self.parser.add_argument( - "-l", "--long", dest="long", - action="store_true", default=False, - help="""Show URL and kind of package.""") + "-l", + "--long", + dest="long", + action="store_true", + default=False, + help="""Show URL and kind of package.""", + ) self.parser.add_argument( - "-s", "--status", dest="status", - action="store_true", default=False, + "-s", + "--status", + dest="status", + action="store_true", + default=False, help=textwrap.dedent("""\ Show checkout status. The first column in the output shows the checkout status: @@ -431,18 +566,24 @@ def __init__(self, develop): ' ' in auto-checkout list and checked out '~' not in auto-checkout list, but checked out '!' in auto-checkout list, but not checked out - 'C' the repository URL doesn't match""")) - self.parser.add_argument("package-regexp", nargs="*", - help="A regular expression to match package names.") + 'C' the repository URL doesn't match"""), + ) + self.parser.add_argument( + "package-regexp", + nargs="*", + help="A regular expression to match package names.", + ) self.parser.set_defaults(func=self) def __call__(self, args): sources = self.develop.sources auto_checkout = self.develop.auto_checkout - packages = self.get_packages(getattr(args, 'package-regexp'), - auto_checkout=args.auto_checkout, - checked_out=args.checked_out, - develop=args.develop) + packages = self.get_packages( + getattr(args, "package-regexp"), + auto_checkout=args.auto_checkout, + checked_out=args.checked_out, + develop=args.develop, + ) workingcopies = self.get_workingcopies(sources) for name in sorted(packages): source = sources[name] @@ -462,7 +603,7 @@ def __call__(self, args): else: info.append("#") if args.long: - info.append("({}) {} {}".format(source['kind'], name, source['url'])) + info.append("({}) {} {}".format(source["kind"], name, source["url"])) else: info.append(name) print(" ".join(info)) @@ -472,8 +613,8 @@ class CmdPony(Command): def __init__(self, develop): Command.__init__(self, develop) self.parser = self.develop.parsers.add_parser( - "pony", - description="It should be easy to develop a pony!") + "pony", description="It should be easy to develop a pony!" + ) self.parser.set_defaults(func=self) def __call__(self, args): @@ -498,6 +639,7 @@ def __call__(self, args): `""""` `""""` ;' ''' import time + logger.info("Starting to develop a pony.") for line in pony.split("\n"): time.sleep(0.25) @@ -508,27 +650,39 @@ def __call__(self, args): class CmdPurge(Command): def __init__(self, develop): Command.__init__(self, develop) - description = textwrap.dedent("""\ + description = textwrap.dedent( + """\ Remove checked out packages which aren't active anymore. - Only 'svn' packages can be purged, because other repositories may contain unrecoverable files even when not marked as 'dirty'.""") + Only 'svn' packages can be purged, because other repositories may contain unrecoverable files even when not marked as 'dirty'.""" + ) self.parser = self.develop.parsers.add_parser( - "purge", - formatter_class=HelpFormatter, - description=description) - self.develop.parsers._choices_actions.append(ChoicesPseudoAction( - "purge", help=description)) + "purge", formatter_class=HelpFormatter, description=description + ) + self.develop.parsers._choices_actions.append( + ChoicesPseudoAction("purge", help=description) + ) self.parser.add_argument( - "-n", "--dry-run", dest="dry_run", - action="store_true", default=False, - help="""Don't actually remove anything, just print the paths which would be removed.""") + "-n", + "--dry-run", + dest="dry_run", + action="store_true", + default=False, + help="""Don't actually remove anything, just print the paths which would be removed.""", + ) self.parser.add_argument( - "-f", "--force", dest="force", - action="store_true", default=False, - help="""Force purge even if the working copy is dirty or unknown (non-svn).""") + "-f", + "--force", + dest="force", + action="store_true", + default=False, + help="""Force purge even if the working copy is dirty or unknown (non-svn).""", + ) self.parser.add_argument( - "package-regexp", nargs="*", - help="A regular expression to match package names.") + "package-regexp", + nargs="*", + help="A regular expression to match package names.", + ) self.parser.set_defaults(func=self) def handle_remove_readonly(self, func, path, exc): @@ -541,8 +695,7 @@ def handle_remove_readonly(self, func, path, exc): def __call__(self, args): buildout_dir = self.develop.buildout_dir - packages = self.get_packages(getattr(args, 'package-regexp'), - checked_out=True) + packages = self.get_packages(getattr(args, "package-regexp"), checked_out=True) packages = packages - self.develop.auto_checkout packages = packages - set(self.develop.develeggs) force = args.force @@ -552,16 +705,21 @@ def __call__(self, args): logger.info("Dry run, nothing will be removed.") for name in packages: source = self.develop.sources[name] - path = source['path'] + path = source["path"] if path.startswith(buildout_dir): - path = path[len(buildout_dir) + 1:] + path = path[len(buildout_dir) + 1 :] need_force = False - if source['kind'] != 'svn': + if source["kind"] != "svn": need_force = True - logger.warn(f"The directory of package '{name}' at '{path}' might contain unrecoverable files and will not be removed without --force.") - if workingcopies.status(source) != 'clean': + logger.warn( + f"The directory of package '{name}' at '{path}' might contain unrecoverable files and will not be removed without --force." + ) + if workingcopies.status(source) != "clean": need_force = True - logger.warn("The package '%s' is dirty and will not be removed without --force." % name) + logger.warn( + "The package '%s' is dirty and will not be removed without --force." + % name + ) if need_force: if not force: continue @@ -569,18 +727,22 @@ def __call__(self, args): # have actually added the --force argument on the # command line. if not force_all: - answer = yesno("Do you want to purge it anyway?", default=False, all=True) + answer = yesno( + "Do you want to purge it anyway?", default=False, all=True + ) if not answer: logger.info("Skipped purge of '%s'." % name) continue - if answer == 'all': + if answer == "all": force_all = True logger.info(f"Removing package '{name}' at '{path}'.") if not args.dry_run: - shutil.rmtree(source['path'], - ignore_errors=False, - onerror=self.handle_remove_readonly) + shutil.rmtree( + source["path"], + ignore_errors=False, + onerror=self.handle_remove_readonly, + ) class CmdRebuild(Command): @@ -588,11 +750,14 @@ def __init__(self, develop): Command.__init__(self, develop) description = "Run buildout with the last used arguments." self.parser = self.develop.parsers.add_parser( - "rebuild", - description=description) - self.develop.parsers._name_parser_map["rb"] = self.develop.parsers._name_parser_map["rebuild"] - self.develop.parsers._choices_actions.append(ChoicesPseudoAction( - "rebuild", "rb", help=description)) + "rebuild", description=description + ) + self.develop.parsers._name_parser_map["rb"] = ( + self.develop.parsers._name_parser_map["rebuild"] + ) + self.develop.parsers._choices_actions.append( + ChoicesPseudoAction("rebuild", "rb", help=description) + ) self.parser.set_defaults(func=self) def __call__(self, args): @@ -610,30 +775,47 @@ def __init__(self, develop): self.parser = self.develop.parsers.add_parser( "reset", help="Resets the packages develop status.", - description="Resets the packages develop status. This is useful when switching to a new buildout configuration.") + description="Resets the packages develop status. This is useful when switching to a new buildout configuration.", + ) self.parser.add_argument( - "-a", "--auto-checkout", dest="auto_checkout", - action="store_true", default=False, - help="""Only considers packages declared by auto-checkout. If you don't specify a then all declared packages are processed.""") + "-a", + "--auto-checkout", + dest="auto_checkout", + action="store_true", + default=False, + help="""Only considers packages declared by auto-checkout. If you don't specify a then all declared packages are processed.""", + ) self.parser.add_argument( - "-c", "--checked-out", dest="checked_out", - action="store_true", default=False, - help="""Only considers packages currently checked out. If you don't specify a then all checked out packages are processed.""") + "-c", + "--checked-out", + dest="checked_out", + action="store_true", + default=False, + help="""Only considers packages currently checked out. If you don't specify a then all checked out packages are processed.""", + ) self.parser.add_argument( - "-d", "--develop", dest="develop", - action="store_true", default=False, - help="""Only considers packages currently in development mode. If you don't specify a then all develop packages are processed.""") + "-d", + "--develop", + dest="develop", + action="store_true", + default=False, + help="""Only considers packages currently in development mode. If you don't specify a then all develop packages are processed.""", + ) self.parser.add_argument( - "package-regexp", nargs="*", - help="A regular expression to match package names.") + "package-regexp", + nargs="*", + help="A regular expression to match package names.", + ) self.parser.set_defaults(func=self) def __call__(self, args): config = self.develop.config - packages = self.get_packages(getattr(args, 'package-regexp'), - auto_checkout=args.auto_checkout, - checked_out=args.checked_out, - develop=args.develop) + packages = self.get_packages( + getattr(args, "package-regexp"), + auto_checkout=args.auto_checkout, + checked_out=args.checked_out, + develop=args.develop, + ) changed = False for name in sorted(packages): if name in config.develop: @@ -641,7 +823,9 @@ def __call__(self, args): logger.info("Reset develop state of '%s'." % name) changed = True if changed: - logger.warn("Don't forget to run buildout again, so the deactived packages are actually not used anymore.") + logger.warn( + "Don't forget to run buildout again, so the deactived packages are actually not used anymore." + ) config.save() @@ -651,7 +835,8 @@ def __init__(self, develop): self.parser = self.develop.parsers.add_parser( "status", formatter_class=HelpFormatter, - description=textwrap.dedent("""\ + description=textwrap.dedent( + """\ Shows the status of tracked packages, filtered if is given. The first column in the output shows the checkout status: ' ' in auto-checkout list @@ -668,41 +853,70 @@ def __init__(self, develop): '-' deactivated '!' deactivated, but the package is in the auto-checkout list 'A' activated, but not in list of development packages (run buildout) - 'D' deactivated, but still in list of development packages (run buildout)""")) - self.develop.parsers._name_parser_map["stat"] = self.develop.parsers._name_parser_map["status"] - self.develop.parsers._name_parser_map["st"] = self.develop.parsers._name_parser_map["status"] - self.develop.parsers._choices_actions.append(ChoicesPseudoAction( - "status", "stat", "st", help="Shows the status of tracked packages.")) - self.parser.add_argument( - "-a", "--auto-checkout", dest="auto_checkout", - action="store_true", default=False, - help="""Only considers packages declared by auto-checkout. If you don't specify a then all declared packages are processed.""") - self.parser.add_argument( - "-c", "--checked-out", dest="checked_out", - action="store_true", default=False, - help="""Only considers packages currently checked out. If you don't specify a then all checked out packages are processed.""") - self.parser.add_argument( - "-d", "--develop", dest="develop", - action="store_true", default=False, - help="""Only considers packages currently in development mode. If you don't specify a then all develop packages are processed.""") - self.parser.add_argument( - "-v", "--verbose", dest="verbose", - action="store_true", default=False, - help="""Show output of VCS command.""") - self.parser.add_argument( - "package-regexp", nargs="*", - help="A regular expression to match package names.") + 'D' deactivated, but still in list of development packages (run buildout)""" + ), + ) + self.develop.parsers._name_parser_map["stat"] = ( + self.develop.parsers._name_parser_map["status"] + ) + self.develop.parsers._name_parser_map["st"] = ( + self.develop.parsers._name_parser_map["status"] + ) + self.develop.parsers._choices_actions.append( + ChoicesPseudoAction( + "status", "stat", "st", help="Shows the status of tracked packages." + ) + ) + self.parser.add_argument( + "-a", + "--auto-checkout", + dest="auto_checkout", + action="store_true", + default=False, + help="""Only considers packages declared by auto-checkout. If you don't specify a then all declared packages are processed.""", + ) + self.parser.add_argument( + "-c", + "--checked-out", + dest="checked_out", + action="store_true", + default=False, + help="""Only considers packages currently checked out. If you don't specify a then all checked out packages are processed.""", + ) + self.parser.add_argument( + "-d", + "--develop", + dest="develop", + action="store_true", + default=False, + help="""Only considers packages currently in development mode. If you don't specify a then all develop packages are processed.""", + ) + self.parser.add_argument( + "-v", + "--verbose", + dest="verbose", + action="store_true", + default=False, + help="""Show output of VCS command.""", + ) + self.parser.add_argument( + "package-regexp", + nargs="*", + help="A regular expression to match package names.", + ) self.parser.set_defaults(func=self) def __call__(self, args): auto_checkout = self.develop.auto_checkout sources_dir = self.develop.sources_dir develeggs = self.develop.develeggs - package_regexp = getattr(args, 'package-regexp') - packages = self.get_packages(package_regexp, - auto_checkout=args.auto_checkout, - checked_out=args.checked_out, - develop=args.develop) + package_regexp = getattr(args, "package-regexp") + packages = self.get_packages( + package_regexp, + auto_checkout=args.auto_checkout, + checked_out=args.checked_out, + develop=args.develop, + ) workingcopies = self.get_workingcopies(self.develop.sources) paths = [] for name in sorted(packages): @@ -711,7 +925,7 @@ def __call__(self, args): if name in auto_checkout: print("! %s" % name) continue - paths.append(source['path']) + paths.append(source["path"]) info = [] if not workingcopies.matches(source): info.append("C") @@ -724,9 +938,9 @@ def __call__(self, args): status, output = workingcopies.status(source, verbose=True) else: status = workingcopies.status(source) - if status == 'clean': + if status == "clean": info.append(" ") - elif status == 'ahead': + elif status == "ahead": info.append(">") else: info.append("M") @@ -734,20 +948,20 @@ def __call__(self, args): if name in develeggs: info.append(" ") else: - if source.get('egg', True): + if source.get("egg", True): info.append("A") else: info.append(" ") else: if name not in develeggs: - if not source.get('egg', True): + if not source.get("egg", True): info.append(" ") elif name in auto_checkout: info.append("!") else: info.append("-") else: - if source.get('egg', True): + if source.get("egg", True): info.append("D") else: info.append(" ") @@ -755,10 +969,10 @@ def __call__(self, args): print(" ".join(info)) if args.verbose: if isinstance(output, bytes): - output = output.decode('utf8') + output = output.decode("utf8") output = output.strip() if output: - for line in output.split('\n'): + for line in output.split("\n"): print(" %s" % line) print() @@ -773,42 +987,65 @@ class CmdUpdate(Command): def __init__(self, develop): Command.__init__(self, develop) description = "Updates all known packages currently checked out." - self.parser = self.develop.parsers.add_parser( - "update", - description=description) - self.develop.parsers._name_parser_map["up"] = self.develop.parsers._name_parser_map["update"] - self.develop.parsers._choices_actions.append(ChoicesPseudoAction( - "update", "up", help=description)) - self.parser.add_argument( - "-a", "--auto-checkout", dest="auto_checkout", - action="store_true", default=False, - help="""Only considers packages declared by auto-checkout. If you don't specify a then all declared packages are processed.""") - self.parser.add_argument( - "-d", "--develop", dest="develop", - action="store_true", default=False, - help="""Only considers packages currently in development mode. If you don't specify a then all develop packages are processed.""") - self.parser.add_argument( - "-f", "--force", dest="force", - action="store_true", default=False, - help="""Force update even if the working copy is dirty.""") - self.parser.add_argument( - "-v", "--verbose", dest="verbose", - action="store_true", default=False, - help="""Show output of VCS command.""") - self.parser.add_argument( - "package-regexp", nargs="*", - help="A regular expression to match package names.") + self.parser = self.develop.parsers.add_parser("update", description=description) + self.develop.parsers._name_parser_map["up"] = ( + self.develop.parsers._name_parser_map["update"] + ) + self.develop.parsers._choices_actions.append( + ChoicesPseudoAction("update", "up", help=description) + ) + self.parser.add_argument( + "-a", + "--auto-checkout", + dest="auto_checkout", + action="store_true", + default=False, + help="""Only considers packages declared by auto-checkout. If you don't specify a then all declared packages are processed.""", + ) + self.parser.add_argument( + "-d", + "--develop", + dest="develop", + action="store_true", + default=False, + help="""Only considers packages currently in development mode. If you don't specify a then all develop packages are processed.""", + ) + self.parser.add_argument( + "-f", + "--force", + dest="force", + action="store_true", + default=False, + help="""Force update even if the working copy is dirty.""", + ) + self.parser.add_argument( + "-v", + "--verbose", + dest="verbose", + action="store_true", + default=False, + help="""Show output of VCS command.""", + ) + self.parser.add_argument( + "package-regexp", + nargs="*", + help="A regular expression to match package names.", + ) self.parser.set_defaults(func=self) def __call__(self, args): - packages = self.get_packages(getattr(args, 'package-regexp'), - auto_checkout=args.auto_checkout, - checked_out=True, - develop=args.develop) + packages = self.get_packages( + getattr(args, "package-regexp"), + auto_checkout=args.auto_checkout, + checked_out=True, + develop=args.develop, + ) workingcopies = self.get_workingcopies(self.develop.sources) force = args.force or self.develop.always_checkout - workingcopies.update(sorted(packages), - force=force, - verbose=args.verbose, - submodules=self.develop.update_git_submodules, - always_accept_server_certificate=self.develop.always_accept_server_certificate) + workingcopies.update( + sorted(packages), + force=force, + verbose=args.verbose, + submodules=self.develop.update_git_submodules, + always_accept_server_certificate=self.develop.always_accept_server_certificate, + ) diff --git a/src/mr/developer/common.py b/src/mr/developer/common.py index 4e71ef1..80880e6 100644 --- a/src/mr/developer/common.py +++ b/src/mr/developer/common.py @@ -1,3 +1,5 @@ +from configparser import RawConfigParser + import logging import os import pkg_resources @@ -6,15 +8,13 @@ import re import sys import threading -from configparser import RawConfigParser - logger = logging.getLogger("mr.developer") def print_stderr(s): sys.stderr.write(s) - sys.stderr.write('\n') + sys.stderr.write("\n") sys.stderr.flush() @@ -24,11 +24,11 @@ def which(name_root, default=None): def is_exe(fpath): return os.path.exists(fpath) and os.access(fpath, os.X_OK) - if platform.system() == 'Windows': + if platform.system() == "Windows": # http://www.voidspace.org.uk/python/articles/command_line.shtml#pathext - pathext = os.environ['PATHEXT'] + pathext = os.environ["PATHEXT"] # example: ['.py', '.pyc', '.pyo', '.pyw', '.COM', '.EXE', '.BAT', '.CMD'] - names = [name_root + ext for ext in pathext.split(';')] + names = [name_root + ext for ext in pathext.split(";")] else: names = [name_root] @@ -52,7 +52,7 @@ def version_sorted(inp, *args, **kwargs): Eg.: version-1-0-1 < version-1-0-2 < version-1-0-10 """ - num_reg = re.compile(r'([0-9]+)') + num_reg = re.compile(r"([0-9]+)") def int_str(val): try: @@ -64,7 +64,7 @@ def split_item(item): return tuple([int_str(j) for j in num_reg.split(item)]) def join_item(item): - return ''.join([str(j) for j in item]) + return "".join([str(j) for j in item]) output = [split_item(i) for i in inp] return [join_item(i) for i in sorted(output, *args, **kwargs)] @@ -72,17 +72,18 @@ def join_item(item): def memoize(f, _marker=[]): def g(*args, **kwargs): - name = '_memoize_%s' % f.__name__ + name = "_memoize_%s" % f.__name__ value = getattr(args[0], name, _marker) if value is _marker: value = f(*args, **kwargs) setattr(args[0], name, value) return value + return g class WCError(Exception): - """ A working copy error. """ + """A working copy error.""" class BaseWorkingCopy: @@ -92,14 +93,14 @@ def __init__(self, source): self.source = source def should_update(self, **kwargs): - offline = kwargs.get('offline', False) + offline = kwargs.get("offline", False) if offline: return False - update = self.source.get('update', kwargs.get('update', False)) + update = self.source.get("update", kwargs.get("update", False)) if not isinstance(update, bool): - if update.lower() in ('true', 'yes'): + if update.lower() in ("true", "yes"): update = True - elif update.lower() in ('false', 'no'): + elif update.lower() in ("false", "no"): update = False else: raise ValueError("Unknown value for 'update': %s" % update) @@ -110,17 +111,17 @@ def yesno(question, default=True, all=True): if default: question = "%s [Yes/no" % question answers = { - False: ('n', 'no'), - True: ('', 'y', 'yes'), + False: ("n", "no"), + True: ("", "y", "yes"), } else: question = "%s [yes/No" % question answers = { - False: ('', 'n', 'no'), - True: ('y', 'yes'), + False: ("", "n", "no"), + True: ("y", "yes"), } if all: - answers['all'] = ('a', 'all') + answers["all"] = ("a", "all") question = "%s/all] " % question else: question = "%s] " % question @@ -152,7 +153,7 @@ def worker(working_copies, the_queue): output_lock.acquire() for lvl, msg in wc._output: lvl(msg) - for line in sys.exc_info()[1].args[0].split('\n'): + for line in sys.exc_info()[1].args[0].split("\n"): logger.error(line) working_copies.errors = True output_lock.release() @@ -169,12 +170,12 @@ def worker(working_copies, the_queue): # then all messages are joined. for item in wc._output: lvl = item[0] - msg = ','.join(item[1:]) + msg = ",".join(item[1:]) lvl(msg) - if kwargs.get('verbose', False) and output is not None and output.strip(): + if kwargs.get("verbose", False) and output is not None and output.strip(): if isinstance(output, bytes): - output = output.decode('utf8') + output = output.decode("utf8") print(output) output_lock.release() @@ -186,19 +187,26 @@ def get_workingcopytypes(): global _workingcopytypes if _workingcopytypes is not None: return _workingcopytypes - group = 'mr.developer.workingcopytypes' + group = "mr.developer.workingcopytypes" _workingcopytypes = {} addons = {} for entrypoint in pkg_resources.iter_entry_points(group=group): key = entrypoint.name workingcopytype = entrypoint.load() - if entrypoint.dist.project_name == 'mr.developer': + if entrypoint.dist.project_name == "mr.developer": _workingcopytypes[key] = workingcopytype else: if key in addons: - logger.error("There already is a working copy type addon registered for '%s'.", key) + logger.error( + "There already is a working copy type addon registered for '%s'.", + key, + ) sys.exit(1) - logger.info("Overwriting '%s' with addon from '%s'.", key, entrypoint.dist.project_name) + logger.info( + "Overwriting '%s' with addon from '%s'.", + key, + entrypoint.dist.project_name, + ) addons[key] = workingcopytype _workingcopytypes.update(addons) return _workingcopytypes @@ -206,19 +214,24 @@ def get_workingcopytypes(): def get_commands(): commands = {} - group = 'mr.developer.commands' + group = "mr.developer.commands" addons = {} for entrypoint in pkg_resources.iter_entry_points(group=group): key = entrypoint.name command = entrypoint.load() - if entrypoint.dist.project_name == 'mr.developer': + if entrypoint.dist.project_name == "mr.developer": commands[key] = command else: if key in addons: - logger.error('There already is a command addon registered for "%s".', key) + logger.error( + 'There already is a command addon registered for "%s".', key + ) sys.exit(1) - logger.info('Overwriting "%s" with addon from "%s".', - key, entrypoint.dist.project_name) + logger.info( + 'Overwriting "%s" with addon from "%s".', + key, + entrypoint.dist.project_name, + ) addons[key] = command commands.update(addons) return commands.values() @@ -250,23 +263,28 @@ def process(self, the_queue): def checkout(self, packages, **kwargs): the_queue = queue.Queue() - if 'update' in kwargs: - if isinstance(kwargs['update'], bool): + if "update" in kwargs: + if isinstance(kwargs["update"], bool): pass - elif kwargs['update'].lower() in ('true', 'yes', 'on', 'force'): - if kwargs['update'].lower() == 'force': - kwargs['force'] = True - kwargs['update'] = True - elif kwargs['update'].lower() in ('false', 'no', 'off'): - kwargs['update'] = False + elif kwargs["update"].lower() in ("true", "yes", "on", "force"): + if kwargs["update"].lower() == "force": + kwargs["force"] = True + kwargs["update"] = True + elif kwargs["update"].lower() in ("false", "no", "off"): + kwargs["update"] = False else: - logger.error("Unknown value '%s' for always-checkout option." % kwargs['update']) + logger.error( + "Unknown value '%s' for always-checkout option." % kwargs["update"] + ) sys.exit(1) - kwargs.setdefault('submodules', 'always') - if kwargs['submodules'] in ['always', 'never', 'checkout']: + kwargs.setdefault("submodules", "always") + if kwargs["submodules"] in ["always", "never", "checkout"]: pass else: - logger.error("Unknown value '%s' for update-git-submodules option." % kwargs['submodules']) + logger.error( + "Unknown value '%s' for update-git-submodules option." + % kwargs["submodules"] + ) sys.exit(1) for name in packages: kw = kwargs.copy() @@ -274,7 +292,7 @@ def checkout(self, packages, **kwargs): logger.error("Checkout failed. No source defined for '%s'." % name) sys.exit(1) source = self.sources[name] - kind = source['kind'] + kind = source["kind"] wc = self.workingcopytypes.get(kind)(source) if wc is None: logger.error("Unknown repository type '%s'." % kind) @@ -282,16 +300,18 @@ def checkout(self, packages, **kwargs): update = wc.should_update(**kwargs) if not source.exists(): pass - elif os.path.islink(source['path']): + elif os.path.islink(source["path"]): logger.info("Skipped update of linked '%s'." % name) continue - elif update and wc.status() != 'clean' and not kw.get('force', False): + elif update and wc.status() != "clean" and not kw.get("force", False): print_stderr("The package '%s' is dirty." % name) - answer = yesno("Do you want to update it anyway?", default=False, all=True) + answer = yesno( + "Do you want to update it anyway?", default=False, all=True + ) if answer: - kw['force'] = True - if answer == 'all': - kwargs['force'] = True + kw["force"] = True + if answer == "all": + kwargs["force"] = True else: logger.info("Skipped update of '%s'." % name) continue @@ -300,38 +320,38 @@ def checkout(self, packages, **kwargs): self.process(the_queue) def matches(self, source): - name = source['name'] + name = source["name"] if name not in self.sources: logger.error("Checkout failed. No source defined for '%s'." % name) sys.exit(1) source = self.sources[name] try: - kind = source['kind'] + kind = source["kind"] wc = self.workingcopytypes.get(kind)(source) if wc is None: logger.error("Unknown repository type '%s'." % kind) sys.exit(1) return wc.matches() except WCError: - for line in sys.exc_info()[1].args[0].split('\n'): + for line in sys.exc_info()[1].args[0].split("\n"): logger.error(line) sys.exit(1) def status(self, source, **kwargs): - name = source['name'] + name = source["name"] if name not in self.sources: logger.error("Status failed. No source defined for '%s'." % name) sys.exit(1) source = self.sources[name] try: - kind = source['kind'] + kind = source["kind"] wc = self.workingcopytypes.get(kind)(source) if wc is None: logger.error("Unknown repository type '%s'." % kind) sys.exit(1) return wc.status(**kwargs) except WCError: - for line in sys.exc_info()[1].args[0].split('\n'): + for line in sys.exc_info()[1].args[0].split("\n"): logger.error(line) sys.exit(1) @@ -342,18 +362,20 @@ def update(self, packages, **kwargs): if name not in self.sources: continue source = self.sources[name] - kind = source['kind'] + kind = source["kind"] wc = self.workingcopytypes.get(kind)(source) if wc is None: logger.error("Unknown repository type '%s'." % kind) sys.exit(1) - if wc.status() != 'clean' and not kw.get('force', False): + if wc.status() != "clean" and not kw.get("force", False): print_stderr("The package '%s' is dirty." % name) - answer = yesno("Do you want to update it anyway?", default=False, all=True) + answer = yesno( + "Do you want to update it anyway?", default=False, all=True + ) if answer: - kw['force'] = True - if answer == 'all': - kwargs['force'] = True + kw["force"] = True + if answer == "all": + kwargs["force"] = True else: logger.info("Skipped update of '%s'." % name) continue @@ -364,7 +386,7 @@ def update(self, packages, **kwargs): def parse_buildout_args(args): settings = dict( - config_file='buildout.cfg', + config_file="buildout.cfg", verbosity=0, options=[], windows_restart=False, @@ -373,72 +395,76 @@ def parse_buildout_args(args): ) options = [] version = pkg_resources.get_distribution("zc.buildout").version - if tuple(version.split('.')[:2]) <= ('1', '4'): - option_str = 'vqhWUoOnNDA' + if tuple(version.split(".")[:2]) <= ("1", "4"): + option_str = "vqhWUoOnNDA" else: - option_str = 'vqhWUoOnNDAs' + option_str = "vqhWUoOnNDAs" while args: - if args[0][0] == '-': + if args[0][0] == "-": op = orig_op = args.pop(0) op = op[1:] while op and op[0] in option_str: - if op[0] == 'v': - settings['verbosity'] = settings['verbosity'] + 10 - elif op[0] == 'q': - settings['verbosity'] = settings['verbosity'] - 10 - elif op[0] == 'W': - settings['windows_restart'] = True - elif op[0] == 'U': - settings['user_defaults'] = False - elif op[0] == 'o': - options.append(('buildout', 'offline', 'true')) - elif op[0] == 'O': - options.append(('buildout', 'offline', 'false')) - elif op[0] == 'n': - options.append(('buildout', 'newest', 'true')) - elif op[0] == 'N': - options.append(('buildout', 'newest', 'false')) - elif op[0] == 'D': - settings['debug'] = True - elif op[0] == 's': - settings['ignore_broken_dash_s'] = True + if op[0] == "v": + settings["verbosity"] = settings["verbosity"] + 10 + elif op[0] == "q": + settings["verbosity"] = settings["verbosity"] - 10 + elif op[0] == "W": + settings["windows_restart"] = True + elif op[0] == "U": + settings["user_defaults"] = False + elif op[0] == "o": + options.append(("buildout", "offline", "true")) + elif op[0] == "O": + options.append(("buildout", "offline", "false")) + elif op[0] == "n": + options.append(("buildout", "newest", "true")) + elif op[0] == "N": + options.append(("buildout", "newest", "false")) + elif op[0] == "D": + settings["debug"] = True + elif op[0] == "s": + settings["ignore_broken_dash_s"] = True else: raise ValueError("Unkown option '%s'." % op[0]) op = op[1:] - if op[:1] in ('c', 't'): + if op[:1] in ("c", "t"): op_ = op[:1] op = op[1:] - if op_ == 'c': + if op_ == "c": if op: - settings['config_file'] = op + settings["config_file"] = op else: if args: - settings['config_file'] = args.pop(0) + settings["config_file"] = args.pop(0) else: - raise ValueError("No file name specified for option", orig_op) - elif op_ == 't': + raise ValueError( + "No file name specified for option", orig_op + ) + elif op_ == "t": try: int(args.pop(0)) except IndexError: - raise ValueError("No timeout value specified for option", orig_op) + raise ValueError( + "No timeout value specified for option", orig_op + ) except ValueError: raise ValueError("No timeout value must be numeric", orig_op) - settings['socket_timeout'] = op + settings["socket_timeout"] = op elif op: - if orig_op == '--help': - return 'help' - raise ValueError("Invalid option", '-' + op[0]) - elif '=' in args[0]: - option, value = args.pop(0).split('=', 1) - parts = option.split(':') + if orig_op == "--help": + return "help" + raise ValueError("Invalid option", "-" + op[0]) + elif "=" in args[0]: + option, value = args.pop(0).split("=", 1) + parts = option.split(":") if len(parts) == 2: section, option = parts elif len(parts) == 1: - section = 'buildout' + section = "buildout" else: - raise ValueError('Invalid option:', option) + raise ValueError("Invalid option:", option) options.append((section.strip(), option.strip(), value.strip())) else: # We've run out of command-line options and option assignnemnts @@ -451,7 +477,7 @@ class Rewrite: _matcher = re.compile(r"(?P