When distributing a Python module, you need to describe its version number,
setup (verison ='')
of setup.py
so that it can be referenced in pip list
after pip install
mylibrary / __ init__.py
so that it can be referenced in mylibrary.__version__
after ʻimport mylibrary`Two descriptions are required. Of course, writing the same version number in two places is troublesome and a source of mistakes, so I want to do it in one place.
Write the version number in __version__.py
and
mylibrary/__version__.py
__version_info__ = (1, 0, 0)
__version__ = '.'.join(map(str, __version_info__))
I think the configuration of ʻimport __version __ from
setup.pyand
__init __.py` is a common pattern.
There is a problem with this configuration.
In __init__.py
(or the core module imported by __init__.py
),
This is the case when import of non-standard module is described.
mylibrary/__init__.py
from __version__ import __version__
from core import MyClass # mylibrary.Shortcuts for use in MyClass
mylibrary/core.py
import numpy as np #Import of external modules required by MyClass
class MyClass(object):
pas
If you execute setup.py
in this state, ...
Sudden Import Error
> py setup.py install
Traceback (most recent call last):
File "C:\Users\hoge\git\example\setup.py", line 4, in <module>
from mylibrary import __version__
File "C:\Users\hoge\git\example\mylibrary\__init__.py", line 2, in <module>
from .core import MyClass
File "C:\Users\hoge\git\example\mylibrary\core.py", line 1, in <module>
import numpy as np
ImportError: No module named numpy
An ImportError will occur and the mylibrary module cannot be installed.
Even though install_requires for installing numpy is in setup.py
It is a "key in the safe" state that the setup.py
cannot be executed and cannot be installed.
[^ 1]: I was really planning to write this year's Advent Calendar as a "best practice", but just before I found an overwhelmingly easy way, it was totally crap. I'm sorry, so I'll leave it as it is. Please refer to those who are using a transcendental old environment where setup.cfg
cannot be used.
The cause is that if you import __version__.py
, __init__.py
in the same directory will also be imported.
So the solution is to "load __version__.py
without importing ".
setup.py
#!/usr/bin/python
from setuptools import setup, find_packages
# from __version__ import __version__ #Delete, cause of ImportError
import os
packages = find_packages()
ns = dict()
for package in packages:
version_file = os.path.join(package, '__version__.py')
if os.path.exists(version_file):
with open(version_file, mode='rt') as f:
eval(compile(f.read(), version_file, 'exec'), dict(), ns)
break
__version__ = ns['__version__']
del ns
setup(
version=__version__,
)
I won't explain it in detail, but I'm looking for __version__.py
, evaluating it, and extracting the variable __version__
directly.
This way you can avoid importing __init__.py
which causes ʻImportError`.
but,
I'm reluctant to write too much code other than metadata in setup.py
It is not realistic to import a non-standard module with __init__.py
.
It seems that something like "an error occurred when trying to combine the version number description in one place" can easily occur. It's no wonder that there is an opinion that "\ _ \ _ version \ _ \ _ is an anti-pattern".
Write setup.py
as follows.
setup.py
from setuptools import setup
setup()
As you can see, all you have to do is call setup ()
and the settings are empty.
setup ()
retrieves the missing settings from the setup.cfg
file, if any, regardless of whether it is empty or not.
And, setup.cfg
has a description method to refer to by specifying files and variables.
setup.cfg
[metadata]
version = attr: mylibrary.__version__.__version__
This has the same effect as applying the variable __version__
in the file mylibrary / __ version__.py
to the version
insetup ()
.
By the way, with this method as well as importing __version__.py
,
__init__.py
also imported?However, the reference of the variable described in setup.cfg
is the same as setup.py
in "Not the best practice",
It seems to ʻeval ()` the file directly without going through the import mechanism, and it does not cause an ImportError.
In addition to version, most things that were described in setup.py
can be described smartly in setup.cfg
.
Also, not only the metadata of setup ()
but also the settings such as flake8, py.test, and nosetest can be described together.
It also helps keep the directory clean.
Since I put setup.cfg
with much effort, I want to make the most of it.
Recommended Posts