Setuptools: distribute package composed of a single module - python

I'm learning how to distribute python packages using setuptools and I have a problem.
setuptools is setting the name of the folder containing a single python file as the name of my package. Below is the structure of my repository:
gerador_endereco/
-- setup.py
-- my_package/
-- __init__.py
-- gerador_endereco.py
My setup.py is:
setup(
name='gerador_endereco',
version='1.0.4',
author='Michel Metran',
description='API para criação ...',
url='https://github.com/open-dsa/gerador_endereco',
packages=find_packages(),
install_requires=requirements,
)
I understand that setuptools is related to the distribution of packages, composed of several modules. But I know that it is possible to distribute a package composed of a single module, but how can I import the package correctly, without the folder name appearing?
# Install
!pip install gerador-endereco
# Import work using "my_package" directory: bad...
from my_package.gerador_endereco import *
# I'd like import like this!!!
from gerador_endereco import *
# Run
listas = get_list_ceps_bairros(estado='sp', municipio='piracicaba')
The PyPi Package is in https://pypi.org/project/gerador-endereco/

setuptools is related to the distribution of packages, period. To install a module restructure you project:
gerador_endereco/
-- setup.py
-- gerador_endereco.py
and change setup.py; remove
packages=find_packages(),
and add
py_modules = ['gerador_endereco']
instead. See the docs at https://docs.python.org/3/distutils/setupscript.html#listing-individual-modules and https://packaging.python.org/guides/distributing-packages-using-setuptools/?#py-modules

Related

What should my project structure be for distributing a personal python package on PyPi?

I'm trying to distribute a personal python package over PyPi. While it successfully pip installs, there's a ModuleNotFound error when I go to import PythonDebuggerTools locally.
setup.py
from setuptools import setup, find_packages
import codecs
import os
import setuptools
VERSION = '0.0.3'
DESCRIPTION = 'Debugging tools'
LONG_DESCRIPTION = 'A package that gives users access to several debugging functionality to make their development process efficient.'
# Setting up
setup(
name='PythonDebuggerTools',
version=VERSION,
author='Aakash Haran',
author_email='email',
description='Testing installation of Package',
long_description=LONG_DESCRIPTION,
long_description_content_type="text/markdown",
url='https://github.com/Luna-Cake/Logger',
license='MIT',
# packages=setuptools.find_packages(),
py_modules=['PythonDebuggerTools'],
install_requires=[],
)
My project structure looks like this:
>build
>dist
>PythonDebuggerTools-0.0.3.tar.gz
>PythonDebuggerTools
>__init__.py
>logger.py
>PythonDebuggerTools.egg-info
>dependency_links.txt
>PKG-INFO
>SOURCES.txt
>top_level.txt
>README.md
>setup.py
>setup.py~
=======
Any help would be greatly appreciated!
py_modules=['PythonDebuggerTools'],
is for *.py files. There is no PythonDebuggerTools.py so nothing was added to the wheel package.
Your PythonDebuggerTools is an importably directory so declare it as a packge:
packages=['PythonDebuggerTools'],
See https://packaging.python.org/tutorials/packaging-projects/

package_dir in setup.py not working as expected

I'm trying to let users write code as a python module (folder with __init__.py defined) under whatever folder name they see fit. After that I want to install that module as a python package but define the import name myself.
The folder structure would be like this:
project_name/
user_defined_name/
__init__.py
...
setup.py
According to this I should be able to add this to my setup.py to get it working:
setuptools.setup(
package_dir={'my_defined_name': 'user_defined_name'},
packages=['user_defined_name']
)
But the only way that I was able to access the code was by using import user_defined_name. I tried installing the package without -e but that gave the same result. Leaving the packages=['..'] out of the setup functions also did not change the result.
My question is kind of the same as this one and there the only answers seem to be to change folder names and that is something that I would like to avoid. That question mentioned that it might be due to a problem in setuptools but that seemed fixed 3 years ago.
In short, it looks like you need something like that in your setup.py:
setuptools.setup(
package_dir={
'my_defined_name': 'user_defined_name',
},
packages=[
'my_defined_name',
],
)
as Ilia Novoselov said in a comment to your question.
This should work, if you package and install the project normally. You would be able to import my_defined_name.
Although, note that as far as I can tell, this will not work if you use an editable installation (python setup.py develop or python -m pip install --editable .). It will be impossible to import my_defined_name, but you would be able to import user_defined_name, which is not what you want.
#Oliver's answer here clarified this for me.
My TLDR is that to support both releases (python setup.py install and pip install .) and editable installs (python setup.py develop and pip install -e .) you must change your file structure to
project_name
setup.py
user_defined_name
my_defined_name
__init__.py
...
docs
tests
and your setup.py to
setuptools.setup(
package_dir={'': 'user_defined_name'},
packages=['my_defined_name']
)
You can support just releases (NOT editable installs) with
project_name
setup.py
user_defined_name
__init__.py
...
docs
tests
setuptools.setup(
package_dir={'my_defined_name': 'user_defined_name'},
packages=['my_defined_name']
)

How to create nested namespace packages for setuptools distribution

I'm developing a python project that will have separately distributable parts.
I have been able to accomplish part of my goal by making a namespace package. I have "sub1" and "sub2", both in namespace "lvl1". I can pip install these in development mode using "pip install -e" or python setup.py develop. I can import them with import lvl1.sub1 and import lvl1.sub2.
However, the project is massive and calls for nested namespaces. I want to import lvl1.lvl2.sub1 and import lvl1.lvl2.sub2. So both subpackages are in the same namespace ("lvl2"), which is itself in a namespace ("lvl1").
Desired conceptual structure:
lvl1/
lvl2/
sub1/
code.py
more_code.py
...
sub2/
code.py
...
Is there a way to do this and how?
Yes there is more than one way. Please read section "Nested namespace packages" in PEP 420.
In python >= 3.3, the easiest way to make nested namespace is to delete (do not include) file __init__.py in the specific folders ("lvl1" and "lvl2") in every distributable parts. In each of the setup.py, explicitly list all the packages in the deepest namespace.
"lvl1_part1/setup.py"
setup(
name='lvl1_part1',
...
zip_safe=False,
packages=['lvl1.lvl2.sub1']
)
"lvl1_part2/setup.py"
setup(
name='lvl1_part2',
...
zip_safe=False,
packages=['lvl1.lvl2.sub2']
)
The file structure for testing:
lvl1_part1/
setup.py
lvl1/
lvl2/
sub1/
__init__.py
lvl1_part2/
setup.py
lvl1/
lvl2/
sub2/
__init__.py
To make the above packages compatible to older python versions, please add the pkgutil magic file to each of the "lvl1" and "lvl2" folders.
Credits: The example above is modified from https://github.com/pypa/sample-namespace-packages/tree/master/pkgutil

How to distribute python module under a specific name

I have python module, named models.py which i would like to upload to PyPi. When afterwards i install it using pip i would like it to appear as a package.
To explain myself, I have following project structure:
my_utils
mapper/
models.py
MANIFEST.in
setup.py
README
logger/
logger.py
logger_conf.json
MANIFEST.in
README
setup.py
I would like to create a distributed package out mapper.models, but when it is being installed on target machine, i would like it to appear in site-packages under mapper_tools.models.
My setup.py:
from distutils.core import setup
setup(
name='mapper_tools',
version='0.1.0',
description='some description',
author='myname',
author_email='my#email.com',
url='https://github.com/mapper-tools',
py_modules=['models'],
)
My MANIFEST.in:
include models.py README
Currently, after running pip install mapper-tools, I find models.py right under site-packages and I would like it to appear under mapper_tools.
Can i specify the structure that should be installed without changing the layout of my project?

How to include license file in setup.py script?

I have written a Python extension module in C++.
I plan to distribute the module with setuptools.
There will be binary distributions for 32- and 64-bit Windows (built with setup.py bdist_egg) and a source distribution for UNIX-like platforms (built with setup.py sdist).
I plan to license the module under the BSD license.
In my source tree, the file LICENSE.txt is in the top folder along with setup.py.
How should I include it in the installation package?
I tried the following setup.py script:
from setuptools import setup, Extension
from glob import glob
setup(
name = 'Foo',
version = '0.1.0',
ext_modules = [Extension('Foo', glob('Source/*.cpp'))],
package_data = {'': ['LICENSE.txt']}
)
It did not work, the license file is not included in the installation package.
Maybe because the setup.py file does not define any packages,
only a single extension module.
How do I fix this?
Write a setup.cfg file and in there specify:
[metadata]
license_files = LICENSE.txt
For this to work it seems like wheel is required to be installed. That is:
pip install wheel
If you have wheel already installed and it doesn't work, try to update it:
pip install --upgrade wheel
Then when installing the package via pip install <path> the LICENSE file gets included.
Since setuptools 42.0.0 you can use the license_files key to specify a list of license files to be included into a distribution. Since version 56.0.0 it supports pattern matching and defaults to ('LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*').
Note that due to implementation details there's actually no need to put this key into setup.cfg file (as another answer suggests). You could supply it as an argument to setup() function instead:
(documentation was unclear on this at the time of writing)
from setuptools import setup
setup(
...
license_files = ('LICENSE.txt',),
...
)
Also note that while these files will be included in both binary (wheel) and source distributions, they won't be installed with your package from setup.py-style source distribution if the user doesn't have a wheel package installed!
To ensure the license files will be installed along with your package you need to make some additional modifications to your setup script:
from setuptools import setup
from setuptools.command.egg_info import egg_info
class egg_info_ex(egg_info):
"""Includes license file into `.egg-info` folder."""
def run(self):
# don't duplicate license into `.egg-info` when building a distribution
if not self.distribution.have_run.get('install', True):
# `install` command is in progress, copy license
self.mkpath(self.egg_info)
self.copy_file('LICENSE.txt', self.egg_info)
egg_info.run(self)
setup(
...
license_files = ('LICENSE.txt',),
cmdclass = {'egg_info': egg_info_ex},
...
)
If your project is a pyproject.toml-style project and you think it will be installed by PEP 517-compatible frontend (e.g. pip>=19), a wheel will be forcibly built from your sources and the license files will be installed into .dist-info folder automatically.
Since version 61.0.0 you could specify project metadata and other configuration options in pyproject.toml file instead.
Using a METADATA.in file, the license can be included both the source package and wheels automatically:
METADATA.in
include README.md
include COPYING
Check out an example here:
https://github.com/node40/smsh
New setuptools (40.x) allows metadata, including license, to be stored in the setup.cfg's "metadata" section. If you use older setuptools you could provide license using the "license" named argument in your setup():
def read_text(file_name: str):
return open(os.path.join(base_path, file_name)).read()
setup(
name = 'Foo',
version = '0.1.0',
ext_modules = [Extension('Foo', glob('Source/*.cpp'))],
# package_data = {'': ['LICENSE.txt']}
license=read_text("LICENSE.txt")
)
You have to move the LICENSE.txt file into the package directory for your project. It cannot reside the top level. Python directories get deployed, not the deployment artifact. If you create a python package, that package actually contains a number of subpackages. Each subpackage must contain ALL the files relevant to deployment.
Do not use data_files as it will actually distribute the files as a separate package. (I've heard package_files works, but I have yet to see a working example to do this).
For example:
setup(
...
license="ZPL",
classifiers=[
...
'License :: OSI Approved :: Zope Public License',
...
],
...)
additionally you can insert your licence text into 'long_description':
setup(
...
long_description="Package description. \nLicense Text",
...)

Categories