I have a private pip package located in my virtual environment site-packages folder and I'd like to cythonize it for a speed boost and added protection.
My script successfully converts the files to .c, however, it places the build/ folder for the temporary .so files locally. It then tries to copy those .so files locally to a folder that does not exist. Instead, I want it to copy those files over to the venv site-packages/ python package where it had just created the .c files.
Is there a way I can specify where to copy those files over?
Or can I specify where that build folder gets created?
my_app/ (working dir/main program folder)
├── app_gui/ (my application)
├── build/ (build folder generated by cython)
virtualenvs/lib/python/site-packages/
├── my_pip_installed_package/
├── folder/
def main():
all_files = get_all_files(BASE_DIR)
py_files = [file for file in all_files if file.suffix =='.py']
py_file_strings = convert_to_str(py_files)
setup(ext_modules=cythonize(py_file_strings, compiler_directives={"language_level": "3"}))
I found what I would call a workaround to do this. Instead of running my cythonize script locally on the main application, I changed directories to the site-packages folder, changed where BASE_DIR points to and ran the script from there.
Related
What files can I delete after creating .exe file with pyinstaller in order not to damage the application?
The directory contains:
.idea/
__pycache__/
build/
dist/
venv/
main.py
main.spec
...as also shown in this graphical listing:
You can delete build and dist, after you got the exe out. __pycache__ is created by python every time you run the application.
Background
I'm developing a python package with roughly the following directory structure:
mossutils/
setup.py
mossutils/
__init__.py
init.py
data/
style.css
script.js
...
My package's setup.py declares console_scripts and includes package_data files:
setup(
name='mossutils',
packages=['mossutils'],
package_data={"mossutils": ["data/*"]},
entry_points = {
"console_scripts": ['mu-init = mossutils.init:main']
},
...)
Installing the package via pip install works as expected: everything is installed in my Python's Lib\site-packages, including the data directory and all files in it, and script mu-init can be executed from the shell (or rather, command prompt, since I'm using Windows).
Goal
Script mu-init is supposed to do some kind of project scaffolding in the current working directory it is invoked from. In particular, it should copy all package_data files (data/style.css, data/script.js, ...) to the current directory.
Solution Attempt
Using module pkgutil, I can read the content of a file, e.g.
import pkgutil
...
data = pkgutil.get_data(__name__, "data/style.css")
Questions
Is there a way for my init.py script to iterate over the contents of the data directory, without hard-coding the file names (in init.py)?
Can the files from the data directory be copied to the current working directory, without opening the source file, reading the content, and writing it to a destination file?
You can get the list of files in the directory using pkg_resources library, which is distributed together with setuptools.
import pkg_resources
pkg_resources.resource_listdir("mossutils", "data")
Project tree:
$.
├── happy_birthday-art.txt
├── happy_birthday.py
├── MANIFEST.in
├── README.rst
└── setup.py
setup.py
from setuptools import setup
setup(
name='Happy_birthday',
py_modules=['happy_birthday'],
data_files=['happy_birthday-art.txt'],
entry_points={
'console_scripts': ['happy_birthday = happy_birthday:main', ],},
long_description=open('README.rst').read(),
)
Now when I do python setup.py sdist and then pip install the created .tar.gz file in a virtual environment I get the following message:
warning: install_data: setup script did not provide a directory for 'happy-birthday-art.txt' -- installing right in '/home/username/.virtualenvs/happy_birthday'
The program uses that .txt file so it fails when trying to run it afterwards.
But I don't want to install happy_birthday-art.txt into a separate folder. I want to install it in the folder where happy_birthday.py is installed. Also, I don't want to have to use absolute paths in setup.py. How do I best set up my setup.py file?
If you have a single-file module like this, no folder will be created, your .py file will be moved directly into the directory which contains the other python modules (/usr/lib/pythonX.X/site-packages/, for example). That's why you have to create a directory:
$ .
|-- happy_birthday/
|-- __init__.py
|-- art.txt
|-- MANIFEST.in
|-- README.rst
|-- setup.py
http://docs.python.org/2/distutils/setupscript.html
"You can specify the data_files options as a simple sequence of files
without specifying a target directory, but this is not recommended,
and the install command will print a warning in this case. To install
data files directly in the target directory, an empty string should be
given as the directory."
However, here the target directory is NOT the site-packages folder, but the
prefix folder, that is the root of the venv. If you'd want the .txt reside in the site-packages dir bare, then it would look not only ugly but seem that really not supported. On the other hand it is possible to install it to another location in the env, for example in "share/doc/foo":
data_files=[('share/doc/foo', ['happy_birthday-art.txt'])],
I'm doing a project with this layout:
project/
bin/
my_bin.py
CHANGES.txt
docs/
LICENSE.txt
README.txt
MANIFEST.in
setup.py
project/
__init__.py
some_thing.py
default_data.json
other_datas/
default/
other_default_datas.json
And the problem is that when I install this using pip, it puts the "default_data.json" and the "other_datas" folder not in the same place as the rest of the app.
How am I supposed to do to make them be in the same place?
They end up on "/home/user/.virtualenvs/proj-env/project"
instead of "/home/user/.virtualenvs/proj-env/lib/python2.6/site-packages/project"
In the setup.py I'm doing it like this:
inside_dir = 'project'
data_folder= os.path.join(inside_dir,'other_datas')
data_files = [(inside_dir, [os.path.join(inside_dir,'default_data.json')])]
for dirpath, dirnames, filenames in os.walk(data_folder):
data_files.append([dirpath, [os.path.join(dirpath, f) for f in filenames]])
From https://docs.python.org/3.4/distutils/setupscript.html#installing-additional-files:
If directory is a relative path, it is interpreted relative to the installation prefix (Python’s sys.prefix for pure-Python packages, sys.exec_prefix for packages that contain extension modules).
Each file name in files is interpreted relative to the setup.py script at the top of the package source distribution.
So the described behavior is simply how data_files work.
If you want to include the data files within your package you need to use package_data instead:
package_data={'project': ['default_data.json', 'other_datas/default/*.json']}
Take a look at this package https://pypi.python.org/pypi/datafolder. It makes it easy to install and use (data files: *.conf, *.ini *.db, ...) by your package and by the user.
Change your MANIFEST.in to include those .json.
It is probably gonna work:
recursive-include project/ *.json
If all of my __init__.py files are empty, do I have to store them into version control, or is there a way to make distutils create empty __init__.py files during installation?
In Python, __init__.py files actually have a meaning! They mean that the folder they are in is a Python module. As such, they have a real role in your code and should most probably be stored in Version Control.
You could well imagine a folder in your source tree that is NOT a Python module, for example a folder containing only resources (e.g. images) and no code. That folder would not need to have a __init__.py file in it. Now how do you make the difference between folders where distutils should create those files and folders where it should not ?
Is there a reason you want to avoid putting empty __init__.py files in version control? If you do this you won't be able to import your packages from the source directory wihout first running distutils.
If you really want to, I suppose you can create __init__.py in setup.py. It has to be before running distutils.setup, so setup itself is able to find your packages:
from distutils import setup
import os
for path in [my_package_directories]:
filename = os.path.join(pagh, '__init__.py')
if not os.path.exists(filename):
init = open(filename, 'w')
init.close()
setup(
...
)
but... what would you gain from this, compared to having the empty __init__.py files there in the first place?