Running a flask app that uses multiple conda environments - python

As the title says, I want to either run multiple conda environements from 1 flask app such that certain pages use 1 version of packages and the others use a different version of packages.
Alternatively, I could do something where I run 2 apps concurrently and then would need to be able to properly redirect from one to another.
I scoured the internet and didn't find anything. Any ideas/documentation on where to get started?
EDIT I was told this was a bad idea and to elaborate on the problem rather than my attempted solution
The problem is that I have certain packages that I am trying to interact with 2 different ML models that were done in different versions of scikit. I can't recreate the model because it was given to me by a coworker. Additionally I am doing some name matching using fuzzywuzzy which is causing issues with other packages I need.

You can do what you are asking by installing both versions to different locations (so they don't overwrite each other), and then renaming the package as this seems to be your only option.
Take the following example, I am going to setup 2 virtual environments, in the first I'll install scitkit-learn 0.22.2 and in the second I'll install 0.20.4, then move the name of the package so python can differentiate them and print the version ($ denotes something to enter on the command line):
$ python3 -m venv sk1
$ source sk1/bin/activate
$ pip3 install scikit-learn==0.22.2 # install to venv 1
$ deactivate # leave
$ python3 -m venv sk2
$ source sk2/bin/activate
$ pip3 install scikit-learn==0.20.4 # install to venv 2
$ deactivate
# move the package names
$ mv ./sk1/lib/python3.7/site-packages/sklearn ./sk1/lib/python3.7/site-packages/sklearn0222
$ mv ./sk2/lib/python3.7/site-packages/sklearn ./sk2/libpython3.7/site-packages/sklearn0204
# add both of them to your PYTHONPATH
$ export PYTHONPATH=$PYTHONPATH:$(pwd)/sk1/lib/python3.7/site-packages/sklearn0222
$ export PYTHONPATH=$PYTHONPATH:$(pwd)/sk2/lib/python3.7/site-packages/sklearn0204
Now let's go into the python interpreter, import them:
$ python3
>>> import sklearn0222 as sk0222
>>> import sklearn0204 as sk0204
>>> sk0222.__version__
'0.22.2'
>>> sk0204.__version__
'0.20.4'
This will use the packages version specific code to run, but you have to be SUPER CAREFUL when referencing each and you cannot use both packages within the same module. so in mymodule1.py you can import sklearn0222 and use its submodules and in mymodule2.py you can import sklearn0204 and use its submodules, but if you try to use both in the same module in your program the second will not be recognized.
Again, this is a bad idea but this is a way to get what you are looking for.

Related

Managing sys.path for multiple developers

The problem I'm facing is small but annoying:
A colleague is working on one project in version control system X (VCS-X).
Another colleague is working in another version control system Y and uses the packages from X.
Unfortunately colleague in VCS-X uses local import and modifies his path using sys.path.append('trunk/my_location') in their code.
My view is that this is wrong practice as colleagues in X forces colleague Y to edit the code prior to being able to run it, merely because their repo is named differently.
How should these dependencies be managed?
Example:
Developer X:
>>> sys.path.append('my_repo/my_location')
>>> from my_location import toolbox
>>> nosetests -v
toolbox.test1 ... ok
toolbox.test2 ... ok
...
Developer Y:
Step 1:
>>> nosetests -v
toolbox.test1 ... fail
...
Step 2:
>>> sys.path.append('my_repo/my_location')
>>> from my_location import toolbox
Import error: No such package.
Step 3:
>>> sys.path.append('my_colleagues_repo/my_location')
>>> from my_location import toolbox
>>> nosetests -v
toolbox.test1 ... ok
toolbox.test2 ... ok
"...Sigh follows; the code is working ..."
Nobody should be doing sys.path.append! This is a workflow problem that you should address first and foremost.
In this situation it will be appropriate to package the toolbox into a distribution. The developer who just wants to use the code from toolbox, i.e. via an import statement or command line script, will execute:
pip install --user toolbox
The developer who wants to work on the toolbox code should also be using pip install. However, this developer should clone the repo, create/activate a virtual environment, and execute:
pip install --editable .
In both situations, pip will sort out the necessary sys.path stuff for you in the correct way.
Follow the PyPA Python Packaging User Guide for the details on how to create a distribution.

How do I set the installation directory for pip?

How can I set the installation path for pip using get-pip.py to /usr/local/bin/? I can't find any mention in the setup guide or in the command line options.
To clarify I don't mean the path where pip packages are installed, but the path where pip itself is installed (it shuold be in /usr/local/bin/pip).
Edit
I do agree with many of the comments/answers that virtualenv would be a better idea in general. However it simply isn't the best one for me at the moment since it would be too disruptive; many of our user's scripts rely on a python2.7 being magically available; this is not convenient either and should change, but we have been using python since before virtualenv was a thing.
Pip itself is a Python package, and the actual pip command just runs a small Python script which then imports and runs the pip package.
You can edit locations.py to change installation directories, however, as stated above, I highly recommend that you do not do this.
Pip Command
Pip accepts a flag, '--install-option="--install-scripts"', which can be used to change the installation directory:
pip install somepackage --install-option="--install-scripts=/usr/local/bin"
Source method
On line 124 in pip/locations.py, we see the following:
site_packages = sysconfig.get_python_lib()
user_site = site.USER_SITE
You can technically edit these to change the default install path, however, using a virtual environment would be highly preferable. This is then used to find the egglink path, which then finds the dist path (code appended below, from pip/__init__.py).
def egg_link_path(dist):
"""
Return the path for the .egg-link file if it exists, otherwise, None.
There's 3 scenarios:
1) not in a virtualenv
try to find in site.USER_SITE, then site_packages
2) in a no-global virtualenv
try to find in site_packages
3) in a yes-global virtualenv
try to find in site_packages, then site.USER_SITE
(don't look in global location)
For #1 and #3, there could be odd cases, where there's an egg-link in 2
locations.
This method will just return the first one found.
"""
sites = []
if running_under_virtualenv():
if virtualenv_no_global():
sites.append(site_packages)
else:
sites.append(site_packages)
if user_site:
sites.append(user_site)
else:
if user_site:
sites.append(user_site)
sites.append(site_packages)
for site in sites:
egglink = os.path.join(site, dist.project_name) + '.egg-link'
if os.path.isfile(egglink):
return egglink
def dist_location(dist):
"""
Get the site-packages location of this distribution. Generally
this is dist.location, except in the case of develop-installed
packages, where dist.location is the source code location, and we
want to know where the egg-link file is.
"""
egg_link = egg_link_path(dist)
if egg_link:
return egg_link
return dist.location
However, once again, using a virtualenv is much more traceable, and any Pip updates will override these changes, unlike your own virtualenv.
It seems the easiest workaround I found to do this is:
install easy_install; this will go in /usr/local/bin/ as expected; the steps for doing this are listed here; I personally ended up running wget https://bootstrap.pypa.io/ez_setup.py -O - | python
install pip with /usr/local/bin/easy_install pip; this will make pip go in /usr/local/bin

Python module development workflow - setup and build [duplicate]

I'm developing my own module in python 2.7. It resides in ~/Development/.../myModule instead of /usr/lib/python2.7/dist-packages or /usr/lib/python2.7/site-packages. The internal structure is:
/project-root-dir
/server
__init__.py
service.py
http.py
/client
__init__.py
client.py
client/client.py includes PyCachedClient class. I'm having import problems:
project-root-dir$ python
Python 2.7.2+ (default, Jul 20 2012, 22:12:53)
[GCC 4.6.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from server import http
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "server/http.py", line 9, in <module>
from client import PyCachedClient
ImportError: cannot import name PyCachedClient
I didn't set PythonPath to include my project-root-dir, therefore when server.http tries to include client.PyCachedClient, it tries to load it from relative path and fails. My question is - how should I set all paths/settings in a good, pythonic way? I know I can run export PYTHONPATH=... in shell each time I open a console and try to run my server, but I guess it's not the best way. If my module was installed via PyPi (or something similar), I'd have it installed in /usr/lib/python... path and it'd be loaded automatically.
I'd appreciate tips on best practices in python module development.
My Python development workflow
This is a basic process to develop Python packages that incorporates what I believe to be the best practices in the community. It's basic - if you're really serious about developing Python packages, there still a bit more to it, and everyone has their own preferences, but it should serve as a template to get started and then learn more about the pieces involved. The basic steps are:
Use virtualenv for isolation
setuptools for creating a installable package and manage dependencies
python setup.py develop to install that package in development mode
virtualenv
First, I would recommend using virtualenv to get an isolated environment to develop your package(s) in. During development, you will need to install, upgrade, downgrade and uninstall dependencies of your package, and you don't want
your development dependencies to pollute your system-wide site-packages
your system-wide site-packages to influence your development environment
version conflicts
Polluting your system-wide site-packages is bad, because any package you install there will be available to all Python applications you installed that use the system Python, even though you just needed that dependency for your small project. And it was just installed in a new version that overrode the one in the system wide site-packages, and is incompatible with ${important_app} that depends on it. You get the idea.
Having your system wide site-packages influence your development environment is bad, because maybe your project depends on a module you already got in the system Python's site-packages. So you forget to properly declare that your project depends on that module, but everything works because it's always there on your local development box. Until you release your package and people try to install it, or push it to production, etc... Developing in a clean environment forces you to properly declare your dependencies.
So, a virtualenv is an isolated environment with its own Python interpreter and module search path. It's based on a Python installation you previously installed, but isolated from it.
To create a virtualenv, install the virtualenv package by installing it to your system wide Python using easy_install or pip:
sudo pip install virtualenv
Notice this will be the only time you install something as root (using sudo), into your global site-packages. Everything after this will happen inside the virtualenv you're about to create.
Now create a virtualenv for developing your package:
cd ~/pyprojects
virtualenv --no-site-packages foobar-env
This will create a directory tree ~/pyprojects/foobar-env, which is your virtualenv.
To activate the virtualenv, cd into it and source the bin/activate script:
~/pyprojects $ cd foobar-env/
~/pyprojects/foobar-env $ . bin/activate
(foobar-env) ~/pyprojects/foobar-env $
Note the leading dot ., that's shorthand for the source shell command. Also note how the prompt changes: (foobar-env) means your inside the activated virtualenv (and always will need to be for the isolation to work). So activate your env every time you open a new terminal tab or SSH session etc..
If you now run python in that activated env, it will actually use ~/pyprojects/foobar-env/bin/python as the interpreter, with its own site-packages and isolated module search path.
A setuptools package
Now for creating your package. Basically you'll want a setuptools package with a setup.py to properly declare your package's metadata and dependencies. You can do this on your own by by following the setuptools documentation, or create a package skeletion using Paster templates. To use Paster templates, install PasteScript into your virtualenv:
pip install PasteScript
Let's create a source directory for our new package to keep things organized (maybe you'll want to split up your project into several packages, or later use dependencies from source):
mkdir src
cd src/
Now for creating your package, do
paster create -t basic_package foobar
and answer all the questions in the interactive interface. Most are optional and can simply be left at the default by pressing ENTER.
This will create a package (or more precisely, a setuptools distribution) called foobar. This is the name that
people will use to install your package using easy_install or pip install foobar
the name other packages will use to depend on yours in setup.py
what it will be called on PyPi
Inside, you almost always create a Python package (as in "a directory with an __init__.py) that's called the same. That's not required, the name of the top level Python package can be any valid package name, but it's a common convention to name it the same as the distribution. And that's why it's important, but not always easy, to keep the two apart. Because the top level python package name is what
people (or you) will use to import your package using import foobar or from foobar import baz
So if you used the paster template, it will already have created that directory for you:
cd foobar/foobar/
Now create your code:
vim models.py
models.py
class Page(object):
"""A dumb object wrapping a webpage.
"""
def __init__(self, content, url):
self.content = content
self.original_url = url
def __repr__(self):
return "<Page retrieved from '%s' (%s bytes)>" % (self.original_url, len(self.content))
And a client.py in the same directory that uses models.py:
client.py
import requests
from foobar.models import Page
url = 'http://www.stackoverflow.com'
response = requests.get(url)
page = Page(response.content, url)
print page
Declare the dependency on the requests module in setup.py:
install_requires=[
# -*- Extra requirements: -*-
'setuptools',
'requests',
],
Version control
src/foobar/ is the directory you'll now want to put under version control:
cd src/foobar/
git init
vim .gitignore
.gitignore
*.egg-info
*.py[co]
git add .
git commit -m 'Create initial package structure.
Installing your package as a development egg
Now it's time to install your package in development mode:
python setup.py develop
This will install the requests dependency and your package as a development egg. So it's linked into your virtualenv's site-packages, but still lives at src/foobar where you can make changes and have them be immediately active in the virtualenv without re-installing your package.
Now for your original question, importing using relative paths: My advice is, don't do it. Now that you've got a proper setuptools package, that's installed and importable, your current working directory shouldn't matter any more. Just do from foobar.models import Page or similar, declaring the fully qualified name where that object lives. That makes your source code much more readable and discoverable, for yourself and other people that read your code.
You can now run your code by doing python client.py from anywhere inside your activated virtualenv. python src/foobar/foobar/client.py works just as fine, your package is properly installed and your working directory doesn't matter any more.
If you want to go one step further, you can even create a setuptools entry point for your CLI scripts. This will create a bin/something script in your virtualenv that you can run from the shell.
setuptools console_scripts entry point
setup.py
entry_points='''
# -*- Entry points: -*-
[console_scripts]
run-fooobar = foobar.main:run_foobar
''',
client.py
def run_client():
# ...
main.py
from foobar.client import run_client
def run_foobar():
run_client()
Re-install your package to activate the entry point:
python setup.py develop
And there you go, bin/run-foo.
Once you (or someone else) installs your package for real, outside the virtualenv, the entry point will be in /usr/local/bin/run-foo or somewhere simiar, where it will automatically be in $PATH.
Further steps
Creating a release of your package and uploading it PyPi, for example using zest.releaser
Keeping a changelog and versioning your package
Learn about declaring dependencies
Learn about Differences between distribute, distutils, setuptools and distutils2
Suggested reading:
The Hitchhiker’s Guide to Packaging
The pip cookbook
So, you have two packages, the first with modules named:
server # server/__init__.py
server.service # server/service.py
server.http # server/http.py
The second with modules names:
client # client/__init__.py
client.client # client/client.py
If you want to assume both packages are in you import path (sys.path), and the class you want is in client/client.py, then in you server you have to do:
from client.client import PyCachedClient
You asked for a symbol out of client, not client.client, and from your description, that isn't where that symbol is defined.
I personally would consider making this one package (ie, putting an __init__.py in the folder one level up, and giving it a suitable python package name), and having client and server be sub-packages of that package. Then (a) you could do relative imports if you wanted to (from ...client.client import something), and (b) your project would be more suitable for redistribution, not putting two very generic package names at the top level of the python module hierarchy.

entry_points does not create custom scripts with pip or easy_install in Python?

I am using simple entry points to make a custom script, with this in setup.py:
entry_points = {
'my_scripts': ['combine_stuff = mypackage.mymod.test:foo']
}
where mypackage/mymod/test.py contains:
import argh
from argh import arg
#arg("myarg", help="Test arg.")
def foo(myarg):
print "Got: ", myarg
When I install my package using this (in same directory as setup.py)
pip install --user -e .
The entry points do not get processed at all it seems. Why is that?
If I install with distribute easy_install, like:
easy_install --user -U .
then the entry points get processed and it creates:
$ cat mypackage.egg-info/entry_points.txt
[my_scripts]
combine_stuff = mypackage.mymod.test:foo
but no actual script called combine_stuff gets placed anywhere in my bin dirs (like ~/.local/bin/). It just doesn't seem to get made. What is going wrong here? How can I get it to make an executable script, and ideally work with pip too?
The answer was to use console_scripts instead of my_scripts. It was unclear that the scripts name was anything other than internal label for the programmer.

How to add new default packages to virtualenv?

When I create a virtualenv, it installs setuptools and pip. Is it possible to add new packages to this list?
Example use cases:
Following this solution to use ipython in virtualenv (from this question) requires installing ipython in every virtualenv (unless I allow system-site-packages).
Or if I'm doing a only flask/pygame/framework development, I'd want it in every virtualenv.
I took a different approach from what is chosen as the correct answer.
I chose I directory, like ~/.virtualenv/deps and installed packages in there by doing
pip install -U --target ~/.virtualenv/deps ...
Next in ~/.virtualenv/postmkvirtualenv I put the following:
# find directory
SITEDIR=$(virtualenvwrapper_get_site_packages_dir)
PYVER=$(virtualenvwrapper_get_python_version)
# create new .pth file with our path depending of python version
if [[ $PYVER == 3* ]];
then
echo "$HOME/.virtualenvs/deps3/" > "$SITEDIR/extra.pth";
else
echo "$HOME/.virtualenvs/deps/" > "$SITEDIR/extra.pth";
fi
Post that basically says the same thing.
You can write a python script, say personalize_venv.py that extends the EnvBuilder class and override its post_setup() method for installing any default packages that you need.
You can get the basic example from https://docs.python.org/3/library/venv.html#an-example-of-extending-envbuilder.
This doesn't need a hook. Directly run the script with command line argument dirs pointing to your venv directory/directories. The hook is the post_setup() method itself of EnvBuilder class.

Categories

Resources