patternpythonMinor
Storing a version number in a Python project
Viewed 0 times
numberversionprojectpythonstoring
Problem
If I have this simple structure:
where I want to define
Now, I also want to use the value stored in
It works, but looks so ugly. Is this really the way to do that?
EDIT:
The
$ tree
.
└── project
├── __init__.py
└── my_script.pywhere I want to define
__version__ in __init__.py (this allows setup.py, in the parent directory, to import project.__version__):$ cat __init__.py
# -*- coding: utf-8 -*-
__version__ = "0.1"Now, I also want to use the value stored in
__version__ from other scripts (possibly in subdirectories). I can write this in my_script.py:#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __init__ import __version__
print(__version__)It works, but looks so ugly. Is this really the way to do that?
EDIT:
The
from . import __version__ unfortunately doesn't work:$ ./my_script.py
Traceback (most recent call last):
File "./my_script.py", line 4, in
from . import __version__
SystemError: Parent module '' not loaded, cannot perform relative importSolution
A better approach is to install your script using a setuptools entry_point and let the argparse handle the parameter parsing. Your project would look something like:
Your setup.py would look like:
When you install your package using pip, it will install a command line script named my-script somewhere like /usr/local/bin. The command line script will load your package and any requirements, then run the
The argparse module handles the
I usually do my __init__.py file a little differently. I follow the what the standard sys module does:
This makes it possible to use
Here's an example of the package in action:
Then to run
.
├── myproject
│ ├── __init__.py
│ └── cli.py
└── setup.pyYour setup.py would look like:
import setuptools
import myproject
setuptools.setup(
name='myproject',
version=myproject.version,
description='Does stuff',
url='https://github.com/me/myproject',
packages=['myproject'],
entry_points={
'console_scripts': ['my-script = myproject.cli:entry_point'],
},
)When you install your package using pip, it will install a command line script named my-script somewhere like /usr/local/bin. The command line script will load your package and any requirements, then run the
entry_point function in your cli module. So let's look at that module:import argparse
import logging
from . import version
def entry_point():
parser = argparse.ArgumentParser(
description='Something ... something short',
epilog='''Bacon ipsum dolor amet occaecat labore qui, ad
veniam boudin capicola flank. Dolore rump jerky magna
sirloin pancetta. Voluptate anim non minim nostrud ham.
Excepteur kevin turkey officia kielbasa pastrami ad ham
nostrud meatball tongue magna t-bone. Chuck salami mollit,
fatback pig velit officia ut. Cupidatat nulla jowl, tail
chicken occaecat excepteur pig beef salami non. Kevin
cupim bresaola filet mignon enim.''')
parser.add_argument('--verbose', '-v', action='count',
help='increase verbosity. Specify multiple times '
'for increased diagnostic output')
parser.add_argument('--version', action='version',
version='%(prog)s {}'.format(version),
help='show the version number and exit')
args = parser.parse_args()
# do stuff here
if __name__ == '__main__':
entry_point()The argparse module handles the
--version and --help arguments for you. Since your code is installed and loaded as a proper package, you can use relative imports. Of course, you are free to use import myproject as well.I usually do my __init__.py file a little differently. I follow the what the standard sys module does:
version_info = (0, 0, 0)
version = '.'.join(str(c) for c in version_info)This makes it possible to use
version_info to compare against the numeric version like version_info > (1, 0) which has some nice benefits.Here's an example of the package in action:
$ python3.4 -mvenv env
$ env/bin/python setup.py install
$ env/bin/my-script --help
usage: my-script [-h] [--verbose] [--version]
Something ... something short
optional arguments:
-h, --help show this help message and exit
--verbose, -v increase verbosity. Specify multiple times for increased
diagnostic output
--version show the version number and exit
Bacon ipsum dolor amet occaecat labore qui, ad veniam boudin capicola flank.
Dolore rump jerky magna sirloin pancetta. Voluptate anim non minim nostrud
ham. Excepteur kevin turkey officia kielbasa pastrami ad ham nostrud meatball
tongue magna t-bone. Chuck salami mollit, fatback pig velit officia ut.
Cupidatat nulla jowl, tail chicken occaecat excepteur pig beef salami non.
Kevin cupim bresaola filet mignon enim. Kielbasa non occaecat flank t-bone ad,
sunt in tail esse deserunt id duis. Ut boudin turducken, pariatur labore
veniam lorem proident culpa laborum. Hamburger swine sunt aliqua, et bacon
shoulder jerky cillum. Meatloaf sed pork belly, dolore corned beef in
consectetur enim ball tip. Fugiat ribeye eiusmod sirloin ground round boudin
shoulder voluptate, reprehenderit tongue landjaeger hamburger. Fatback nisi
occaecat, bacon brisket consequat filet mignon fugiat laboris turducken eu. Ad
tempor frankfurter reprehenderit est corned beef voluptate.
$
$ env/bin/my-script --version
my-script 0.0.0Then to run
myproject/cli.py as a standalone script:$ python -m myproject.cli --version
cli.py 0.0.0Code Snippets
.
├── myproject
│ ├── __init__.py
│ └── cli.py
└── setup.pyimport setuptools
import myproject
setuptools.setup(
name='myproject',
version=myproject.version,
description='Does stuff',
url='https://github.com/me/myproject',
packages=['myproject'],
entry_points={
'console_scripts': ['my-script = myproject.cli:entry_point'],
},
)import argparse
import logging
from . import version
def entry_point():
parser = argparse.ArgumentParser(
description='Something ... something short',
epilog='''Bacon ipsum dolor amet occaecat labore qui, ad
veniam boudin capicola flank. Dolore rump jerky magna
sirloin pancetta. Voluptate anim non minim nostrud ham.
Excepteur kevin turkey officia kielbasa pastrami ad ham
nostrud meatball tongue magna t-bone. Chuck salami mollit,
fatback pig velit officia ut. Cupidatat nulla jowl, tail
chicken occaecat excepteur pig beef salami non. Kevin
cupim bresaola filet mignon enim.''')
parser.add_argument('--verbose', '-v', action='count',
help='increase verbosity. Specify multiple times '
'for increased diagnostic output')
parser.add_argument('--version', action='version',
version='%(prog)s {}'.format(version),
help='show the version number and exit')
args = parser.parse_args()
# do stuff here
if __name__ == '__main__':
entry_point()version_info = (0, 0, 0)
version = '.'.join(str(c) for c in version_info)$ python3.4 -mvenv env
$ env/bin/python setup.py install
$ env/bin/my-script --help
usage: my-script [-h] [--verbose] [--version]
Something ... something short
optional arguments:
-h, --help show this help message and exit
--verbose, -v increase verbosity. Specify multiple times for increased
diagnostic output
--version show the version number and exit
Bacon ipsum dolor amet occaecat labore qui, ad veniam boudin capicola flank.
Dolore rump jerky magna sirloin pancetta. Voluptate anim non minim nostrud
ham. Excepteur kevin turkey officia kielbasa pastrami ad ham nostrud meatball
tongue magna t-bone. Chuck salami mollit, fatback pig velit officia ut.
Cupidatat nulla jowl, tail chicken occaecat excepteur pig beef salami non.
Kevin cupim bresaola filet mignon enim. Kielbasa non occaecat flank t-bone ad,
sunt in tail esse deserunt id duis. Ut boudin turducken, pariatur labore
veniam lorem proident culpa laborum. Hamburger swine sunt aliqua, et bacon
shoulder jerky cillum. Meatloaf sed pork belly, dolore corned beef in
consectetur enim ball tip. Fugiat ribeye eiusmod sirloin ground round boudin
shoulder voluptate, reprehenderit tongue landjaeger hamburger. Fatback nisi
occaecat, bacon brisket consequat filet mignon fugiat laboris turducken eu. Ad
tempor frankfurter reprehenderit est corned beef voluptate.
$
$ env/bin/my-script --version
my-script 0.0.0Context
StackExchange Code Review Q#129683, answer score: 5
Revisions (0)
No revisions yet.