Empty example of a python package with one module, used in an application - which runs in a virtual environment.
We have an application app.py
that uses a function myfunc
in a package mypkg
.
root
+-- app
| +-- app.py
+-- mypkg
+.. mymod.py // implements myfunc()
We want to use a virtual environment to run app.py
, and we want to
install mypkg
in that virtual environment.
Suppose we have a module mymod.py
with the function myfunc()
that we want to use in app.py
:
def myfunc() :
print("myfunc")
In order to make this into a package mypkg
, we need to create a directory
with that name, put the module file there, and add an empty __init__.py
.
A file named __init__.py
marks a directory as a Python package;
typically it is empty, but when a package is imported, __init__.py
is executed.
mypkg
+-- __init__.py
+-- mymod.py
In order to convert this into an installable package, we need to create a container directory with - again - the package name, store some meta files, and store the above created package directory inside.
mypkg
+-- mypkg
| +-- __init__.py
| +-- mymod.py
+-- license.txt // optional
+-- readme.md // optional
+-- setup.py
The setup.py
is mandatory. It contains something like the below.
from setuptools import setup
setup(
name='mypkg',
version='0.1',
description='a test for packaging modules',
author='Maarten Pennings',
packages=['mypkg']
)
The application code app.py
just calls myfunc()
.
Do note that we import that function first.
from mypkg.mymod import myfunc
def main() :
print("myapp")
myfunc()
if __name__ == "__main__":
main()
I like to develop python projects in a virtual environment. This allows different versions of package to be used by different projects. Maybe even more important, environments allow installing packages in those environments, instead of in the global space. This means the global space is not "polluted" by packages, so each project has to install them explicitly.
To protect myself, I do not even have python in my path
.
This way I must create a virtual enviroment.
To create an environment I have a standard script setup.bat
.
You will probably have to change LOCATION
(eg keep it empty
if Python is in your path).
SET LOCATION=C:\Users\maarten\AppData\Local\Programs\Python\Python39\
%LOCATION%python.exe -m venv env
CALL env\Scripts\activate.bat
env\Scripts\python -m pip install --upgrade pip setuptools wheel
IF EXIST requirements.txt (
pip install -r requirements.txt
)
What does differ per project is the requirements.txt
file.
Normally it has lines like numpy
and matplotlib
.
In this simple project it has only a ref to mypkg
.
../mypkg
Finally, I typically have a run.bat
.
@ECHO off
IF "(env) " neq "%PROMPT:~0,6%" ECHO Please run setup.bat first && EXIT /b
python app.py
The files we store in the repo are as follows.
root
+-- app
| +-- app.py
| +-- requirements.txt
| +-- run.bat
| +-- setup.bat
+--mypkg
| +-- license.txt
| +-- readme.md
| +-- setup.py
| +-- mypkg
| +-- mymod.py
| +-- __init__.py
+-- clean.bat
We open cmd
in the app
directory and run setup.bat
.
This is the output.
root> setup
root> SET LOCATION=C:\Users\maarten\AppData\Local\Programs\Python\Python39\
root> C:\Users\maarten\AppData\Local\Programs\Python\Python39\python.exe -m venv env
root> CALL env\Scripts\activate.bat
Collecting pip
Using cached pip-24.0-py3-none-any.whl (2.1 MB)
Collecting setuptools
Using cached setuptools-69.5.1-py3-none-any.whl (894 kB)
Collecting wheel
Using cached wheel-0.43.0-py3-none-any.whl (65 kB)
Installing collected packages: pip, setuptools, wheel
Attempting uninstall: pip
Found existing installation: pip 20.2.3
Uninstalling pip-20.2.3:
Successfully uninstalled pip-20.2.3
Attempting uninstall: setuptools
Found existing installation: setuptools 49.2.1
Uninstalling setuptools-49.2.1:
Successfully uninstalled setuptools-49.2.1
Successfully installed pip-24.0 setuptools-69.5.1 wheel-0.43.0
Processing c:\users\maarten\desktop\pythonpackage\mypkg
Preparing metadata (setup.py) ... done
Building wheels for collected packages: mypkg
Building wheel for mypkg (setup.py) ... done
Created wheel for mypkg: filename=mypkg-0.1-py3-none-any.whl size=1520 sha256=03ae38ab17f1f5cd9590f407cf5842dbe2070650166f2282c753324d1eb68ea8
Stored in directory: C:\Users\maarten\AppData\Local\Temp\pip-ephem-wheel-cache-mebwrils\wheels\f1\43\54\fe5b636ba825d845520f054e0e72b3d2dd5e47ecab4f620c8b
Successfully built mypkg
Installing collected packages: mypkg
Successfully installed mypkg-0.1
(env) root>
The setup
creates a virtual environment in directory app\env
.
The (env)
in the prompt indicates that the virtual environment has been
activated; basically, this means that root\app\env
has been prepended
to the system search path
.
The setup
also creates two directories in mypkg
.
These and env
do not have to be stored in the repo.
+-- readme.md
+-- app
| +-- app.py
| +-- requirements.txt
| +-- run.bat
| +-- setup.bat
| +-- env <=== lots of generated stuff
+-- mypkg
| +-- license.txt
| +-- readme.md
| +-- setup.py
| +-- build <=== lots of generated stuff
| +-- mypk
| | +-- mymod.py
| | +-- __init__.py
| +-- mypkg.egg-info <=== lots of generated stuff
+-- clean.bat
You may use clean.bat
to remove the generated files.
We can now run the app
. We use the helper run.bat
for that.
(env) root> run
myapp
myfunc
(end)