New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Command Line Interface #59
Comments
@mattrohland yes, I'd definitely be interested in something like this and would be happy to get a PR! One thing I should mention – I didn't quite follow Python's state-of-the-art on providing executables with packages, and not sure how to do it best to not irritate our users, not force them running the command in "sudo" etc. In Haskell packages, you can provide an executable description, and upon install it gets installed into the Anyways, if there's a similar popular way to handle things in Python and community is generally happy with it, I'd be glad to receive a PR adding the command-line functionality. Thanks! |
Hello, I'm happy to give a hand if need be? |
@weakcamel if @mattrohland doesn't give his progress update in a day – feel free to take over the work, I'm sure it's not that big task to implement |
What requirements do you accept? Click http://click.pocoo.org/5/ Or just want us to use standard argument parser libray argparse https://docs.python.org/3/library/argparse.html#module-argparse |
Oops. I missed the previous update and then went AWOL myself - sorry about that. Either is good really, as long as the requirements are defined in setup.py |
I personally like "click" http://click.pocoo.org/5/why/ |
It's pretty easy with setuptools and the When installing, use Just need a python script that wraps the main library functions and exposes arguments using something like |
Okey-dokey. So, is anyone else taking a stab at this or is it free for taking? +1 to the setuptools entry point in any case. |
Almost a year later, I'd say it's still free for the taking! 😆 |
@weakcamel , contributions are highly appreciated! |
Here is a possible CLI implementation with argparse """python-semver CLI
Semantic Versioning Command Line Interface"""
import argparse
import semver
def create_parser():
parser = argparse.ArgumentParser(description='Semantic Versioning CLI.')
parser.add_argument('--compare', nargs=2, metavar=('a', 'b'),
help='Compare version `a` with version `b`')
parser.add_argument('--bump_major', nargs=1, metavar='a',
help='Bump major version `a`')
parser.add_argument('--bump_minor', nargs=1, metavar='a',
help='Bump minor version `a`')
parser.add_argument('--bump_patch', nargs=1, metavar='a',
help='Bump patch version `a`')
parser.add_argument('-v', '--version', action='store_true',
help='Print python-semver version')
return parser
def parse_args(args):
if args.compare is not None:
s = semver.compare(*args.compare)
elif args.bump_major is not None:
s = semver.bump_major(*args.bump_major)
elif args.bump_minor is not None:
s = semver.bump_minor(*args.bump_minor)
elif args.bump_patch is not None:
s = semver.bump_patch(*args.bump_patch)
elif args.version:
s = semver.__version__
else:
return
return s
def main():
parser = create_parser()
args = parser.parse_args()
s = parse_args(args)
if s is None:
parser.print_help()
else:
print(s)
if __name__ == '__main__':
main() and tests def test_semver_cli():
parser = create_parser()
args = parser.parse_args(['-v'])
v = parse_args(args)
assert v == __version__
args = parser.parse_args(['--compare', '1.0.0', '2.0.0'])
v = parse_args(args)
assert v == -1
args = parser.parse_args(['--bump_major', '3.4.5'])
v = parse_args(args)
assert v == '4.0.0'
args = parser.parse_args(['--bump_minor', '3.4.5'])
v = parse_args(args)
assert v == '3.5.0'
args = parser.parse_args(['--bump_patch', '3.4.5'])
v = parse_args(args)
assert v == '3.4.6' |
Oki-doki :-) I'm happy to do the mechanics (update |
That's nice @weakcamel ! |
@scls19fr a few questions (I'll try to keep them to a few) as this would be my first contribution here:
|
You can test locally on every environnement using simply
or simply run manually tests and PEP8 checks using
Anyway CI will catch problem for you for others Python versions. Not sure I understand correctly your last question. You need to put cli in a separate file and tests in an other file test_semver.py. |
Great, thanks!
Sorry for an unclear question yet your answer was spot on anyway :-) To clarify, I've been wondering whether I should put the main inside |
the reason why I prefer a separate file for CLI is that we have now doctests in |
I would prefer to use the
You brought up different solutions where to put the CLI interface:
However, we didn't discuss a third solution:
With the third solution, you could add a Nevertheless, I would propose this CLI API (assuming we have a CLI script
It would be easy to extend, for example, adding a "match" subcommand or doing other fancy stuff. My implementation is inspired by @scls19fr and would use the above lines: """
Semantic Versioning Command Line Interface
"""
import argparse
import sys
from semver import compare, parse_version_info
def parsecli(cliargs=None):
"""Parse CLI with :class:`argparse.ArgumentParser` and return parsed result
:param list cliargs: Arguments to parse or None (=use sys.argv)
:return: parsed CLI result and parser instance
:rtype: tuple(:class:`argparse.Namespace`, :class:`argparse.ArgumentParser`)
"""
parser = argparse.ArgumentParser(prog=__package__,
description=__doc__)
s = parser.add_subparsers(help="commands")
# create compare subcommand
parser_compare = s.add_parser("compare",
help="Compares two versions"
)
parser_compare.set_defaults(which="compare")
parser_compare.add_argument("version1",
help="First version"
)
parser_compare.add_argument("version2",
help="First version"
)
# create bump subcommand
parser_bump = s.add_parser("bump",
help="Bumps a version"
)
parser_bump.set_defaults(which="bump")
sb = parser_bump.add_subparsers(title="Bump commands",
dest="bump") #
# Create subparsers for the bump subparser:
for p in (sb.add_parser("major",
help="Bump the major part of the version"),
sb.add_parser("minor",
help="Bump the minor part of the version"),
sb.add_parser("patch",
help="Bump the patch part of the version"),
sb.add_parser("prerelease",
help="Bump the prerelease part of the version"),
sb.add_parser("build",
help="Bump the build part of the version")):
p.add_argument("version",
help="Version to raise"
)
args = parser.parse_args(args=cliargs)
return args, parser
def process(args, parser):
"""Process the input from the CLI
:param args: The parsed arguments
:type args: :class:`argparse.Namespace`
:param parser: the parser instance
:type parser: :class:`argparse.ArgumentParser`
"""
print("args:", args)
if args.which == "bump":
maptable = {'major': 'bump_major',
'minor': 'bump_minor',
'patch': 'bump_patch',
'prerelease': 'bump_prerelease',
'build': 'bump_build',
}
ver = parse_version_info(args.version)
# get the respective method and call it
func = getattr(ver, maptable[args.bump])
print(func())
elif args.which == "compare":
res = compare(args.version1, args.version2)
print(res)
return 0
def main(cliargs=None):
"""Entry point for the application script
:param list cliargs: Arguments to parse or None (=use :class:`sys.argv`)
:return: error code
:rtype: int
"""
try:
return process(*parsecli(cliargs))
except (ValueError, TypeError) as err:
print("ERROR", err, file=sys.stderr)
return 2 |
* Extend setup.py with entry_point key and point to semver.main. The script is named "semver" * Introduce 3 new functions: * createparser: creates and returns an argparse.ArgumentParser instance * process: process the CLI arguments and call the requested actions * main: entry point for the application script; combines parsecli() and process() * Add test cases * sort import lines of semver functions/class with isort tool * sort list of SEMVERFUNCS variable * Extend documentation * Add sphinx-argparse as a doc requirement * Include new cli.rst file which (self)documents the arguments of the semver script with the help of sphinx-argparse * Extend extensions variable in conf.py to be able to use the sphinx-argparse module
* Extend setup.py with entry_point key and point to semver.main. The script is named "semver" * Introduce 3 new functions: * createparser: creates and returns an argparse.ArgumentParser instance * process: process the CLI arguments and call the requested actions * main: entry point for the application script; combines parsecli() and process() * Add test cases * sort import lines of semver functions/class with isort tool * sort list of SEMVERFUNCS variable * Extend documentation * Add sphinx-argparse as a doc requirement * Include new cli.rst file which (self)documents the arguments of the semver script with the help of sphinx-argparse * Extend extensions variable in conf.py to be able to use the sphinx-argparse module
* Extend setup.py with entry_point key and point to semver.main. The script is named "semver" * Introduce 3 new functions: * createparser: creates and returns an argparse.ArgumentParser instance * process: process the CLI arguments and call the requested actions * main: entry point for the application script * Add test cases * sort import lines of semver functions/class with isort tool * sort list of SEMVERFUNCS variable * Extend documentation * Add sphinx-argparse as a doc requirement * Include new cli.rst file which (self)documents the arguments of the semver script with the help of sphinx-argparse * Extend extensions variable in conf.py to be able to use the sphinx-argparse module
* Extend setup.py with entry_point key and point to semver.main. The script is named "semver" * Introduce 3 new functions: * createparser: creates and returns an argparse.ArgumentParser instance * process: process the CLI arguments and call the requested actions * main: entry point for the application script * Add test cases * sort import lines of semver functions/class with isort tool * sort list of SEMVERFUNCS variable * Extend documentation * Add sphinx-argparse as a doc requirement * Include new cli.rst file which (self)documents the arguments of the semver script with the help of sphinx-argparse * Extend extensions variable in conf.py to be able to use the sphinx-argparse module
* Extend setup.py with entry_point key and point to semver.main. The script is named "semver" * Introduce 3 new functions: * createparser: creates and returns an argparse.ArgumentParser instance * process: process the CLI arguments and call the requested actions * main: entry point for the application script * Add test cases * sort import lines of semver functions/class with isort tool * sort list of SEMVERFUNCS variable * Extend documentation * Add sphinx-argparse as a doc requirement * Include new cli.rst file which (self)documents the arguments of the semver script with the help of sphinx-argparse * Extend extensions variable in conf.py to be able to use the sphinx-argparse module
@scls19fr, @k-bx, @mattrohland @weakcamel Feel free to discuss it. 😉 (Sorry for the noise. But for some reasons, there seems to be a conflict in the file |
* Extend setup.py with entry_point key and point to semver.main. The script is named "semver" * Introduce 3 new functions: * createparser: creates and returns an argparse.ArgumentParser instance * process: process the CLI arguments and call the requested actions * main: entry point for the application script * Add test cases * sort import lines of semver functions/class with isort tool * sort list of SEMVERFUNCS variable * Extend documentation * Add sphinx-argparse as a doc requirement * Include new cli.rst file which (self)documents the arguments of the semver script with the help of sphinx-argparse * Extend extensions variable in conf.py to be able to use the sphinx-argparse module
Ok, found it. I forgot to pull from the original, upstream repo, argh, my fault. The conflict is fixed now. |
* Extend setup.py with entry_point key and point to semver.main. The script is named "semver" * Introduce 3 new functions: * createparser: creates and returns an argparse.ArgumentParser instance * process: process the CLI arguments and call the requested actions * main: entry point for the application script * Add test cases * sort import lines of semver functions/class with isort tool * sort list of SEMVERFUNCS variable * Extend documentation * Add sphinx-argparse as a doc requirement * Include new cli.rst file which (self)documents the arguments of the semver script with the help of sphinx-argparse * Extend extensions variable in conf.py to be able to use the sphinx-argparse module * Update CHANGELOG.rst
* Extend setup.py with entry_point key and point to semver.main. The script is named "pysemver" * Introduce 3 new functions: * createparser: creates and returns an argparse.ArgumentParser instance * process: process the CLI arguments and call the requested actions * main: entry point for the application script * Add test cases * sort import lines of semver functions/class with isort tool * sort list of SEMVERFUNCS variable * Extend documentation * Add sphinx-argparse as a doc requirement * Include new cli.rst file which (self)documents the arguments of the semver script with the help of sphinx-argparse * Extend extensions variable in conf.py to be able to use the sphinx-argparse module * Update CHANGELOG.rst
* Extend setup.py with entry_point key and point to semver.main. The script is named "pysemver" * Introduce 3 new functions: * createparser: creates and returns an argparse.ArgumentParser instance * process: process the CLI arguments and call the requested actions * main: entry point for the application script * Add test cases * sort import lines of semver functions/class with isort tool * sort list of SEMVERFUNCS variable * Extend documentation * Add sphinx-argparse as a doc requirement * Include new cli.rst file which (self)documents the arguments of the semver script with the help of sphinx-argparse * Extend extensions variable in conf.py to be able to use the sphinx-argparse module * Update CHANGELOG.rst
I've been working on a continuous integration pipeline that needs to perform very basic operations on an applications current version. The application's pipeline is currently a series of Shell commands strung together and while it would be possible to rework much of the pipeline as a Python script that uses python-semver as-is, extending python-semver with a command line interface ended up being a much lower level of effort.
While the interface I wrote is far from comprehensive, I was wondering if a CLI was something you'd be interested in having as part of the python-semver core package.
Here is a Gist of what I'd written for my immediate needs: https://gist.github.com/mattrohland/43b47bd079bd3d47adf674cbdbd970c3
The text was updated successfully, but these errors were encountered: