organizing fabric tasks in python modules - fabric

I have task A in a.py, B in b.py and task B executes A, hence there's an 'import a' in b.py.
when I do 'fab --list', I get a.A, b.B and b.a.A. How can I avoid b.a.A in the fab list? thanks.

You can limit what fabric will display in fab -l by limiting the functions that exported from your modules. A good description of this is given in the fabric documentation for defining tasks (specifically the sections on namespaces and Limiting with __all__.
If you've got your fabfile in a directory something like this:
.
└── fabfile
├── a.py
├── b.py
└── __init__.py
The brute force way to do this is via renaming in __init__.py:
import a
import b
A = a.A
B = b.B
In a.py (and b.py) you block the export of all functions with __all__
from fabric.api import *
#task
def A():
pass
__all__ = []
Next time you run fab -l you should get the correct results
$ fab -l
Available commands:
A
B
However, it's worth thinking about whether this is something you want to do. Typically if you've got enough functionality to make reading the output of fabric difficult, you may want to consider whether you legitimately want to run those functions as tasks in the first place.
An alternative to the __all__ hack is to get fabric to present the tasks in a different format that, might be, more readable
$ fab --list-format=nested --list
Available commands (remember to call as module.[...].task):
A
B
a:
A
b:
B

Related

Calling a function from inside a sub-package the correct way in python

I have been trying to understand how to properly call a function from inside a subpackage in python. I wanted to be able to call the function the way I call, for example, function isfile from os package, with os.path.isfile(). I made a test package with a structure like this:
sandbox/
-- __init__.py
-- acid.py
-- pack1/
-- __init__.py
-- fly/
-- __init__.py
-- plane.py
-- by/
-- pack2/
There are only two modules there, acid.py and plane.py. Both of them contain just a function, e.g. plane.py is
"""plane module"""
def plane(x):
x=x+4
return x
To use the function in my test.py code, I put
import pack1
in sandbox/__init__.py
import fly
in sandbox/pack1/__init__.py, and
from plane import plane
in sandbox/pack1/fly/__init__.py
The test code was then:
import sandbox
print sandbox.pack1.fly.plane(3)
Is this the right way to import a function from a subpackage, or I'm misunderstanding things?
What you did certainly works, although there are several worthwhile changes to make.
First, a note about importing from packages: importing a module is semantically distinct from accessing something in a module, even though from xml import sax and from datetime import date are syntactically equivalent. It's impossible to import only part of a module, so that
import datetime
datetime.date.today() # OK: date is a class
is guaranteed to work. However, it is possible to import a package but not the modules it contains. This is a good thing for efficiency, but it does mean that
import xml
xml.sax.parse(...) # AttributeError: 'module' object has no attribute 'sax'
is an error. Unfortunately, such errors often go uncaught because some other code has already imported sax, making it available to any other code that imports xml. (The word "from" in from xml import sax is referring to the complete package on disk, not the module object xml — on which it stores the new module as an attribute!)
As an aside, note that your example of os.path is an abberation: writing
import os
os.path.isfile(...)
works, but only because os.path is not actually a module but an alias for one of posixpath, ntpath, etc. (It then gets installed in sys.modules to allow import os.path as if it were a normal module.)
As such, for a package there is a set of public modules that the user must be aware of (because they must be imported by name to be available); the rest are internal modules that the package loads itself when necessary. If a package contains no public modules, it is irrelevant to the user that it is a package (for example, importlib with its one public function is actually implemented as a package for forward compatibility with Python 3).
Now for the suggestions:
Implicit relative imports are deprecated: write from . import pack1 instead of just import pack1 in sandbox/__init__.py, for instance.
The from plane import plane (or from .plane import plane, following the above point) is problematic because it overwrites the reference to the module plane.py with a reference to the function. Instead:
Define the user-visible entry points (like plane()) directly in their package's __init__.py, importing internal functions from private modules as needed, or
Rename the module (to plane_module.py or so) to avoid the collision.
However, it's not generally a good idea to have a package automatically import its public modules anyway: it forces the client to pay for loading unused parts of the package, and it blurs the distinction between public modules and simple nested names. Instead, write client code like
import sandbox.pack1.fly
print sandbox.pack1.fly.plane(3) # the same line you had
or
from sandbox.pack1 import fly
print fly.plane(3)
if you want to avoid repeating sandbox.pack1.
It is often suggested that __init__.py be entirely empty, and in Python 3.3 it became possible to define packages without any __init__.py at all (which by necessity "makes it empty"). This policy does more than the "no automatic import" suggestion in that it precludes loading things from private modules (like plane).
There are sometimes good reasons to have a non-empty __init__.py; for example, it allows reorganzing an existing module into a package without breaking its clients. I personally see no reason to especially restrict its contents; for further discussion see What is __init__.py for?.
The init.py file makes a folder as a package so that you can import it to python prompt. If you just want to call the function "plane" from your file plane.py, add the absolute path of plane.py file to your PYTHONPATH and call the funtion as shown below.
>>> import sys
>>> sys.path.append("D:\\sandbox\\pack1\\fly")
>>> import plane
>>> print plane.__doc__
plane module
>>> print plane.plane(3)
7
If you want all the packages under "sandbox" folder to be used in your script just add the absolute path of "sandbox" folder to your PYTHONPATH and call the funtion as shown below.
>>> import sys
>>> sys.path.append("D:\\")
>>> from sandbox.pack1.fly import plane
>>> print plane.plane(3)
7
You can also import the "plane.py" module and call the function "plane" as shown below:
>>> import sys
>>> sys.path.append("D:\\")
>>> import sandbox.pack1.fly.plane
>>> print sandbox.pack1.fly.plane.plane(3)
7

Only in py.test file, usage of a nested(word?) imported variable/function fails with NameError

I have a python structure like this:
mymodule/
globalconfig.py # variables to set environment, etc
work.py # has: from mymodule.globalconfig import *
__init__.py
tests/
testspart1/
test_work.py # has: from mymodule.work import *
From inside work.py, all is well and I can access my global config variables and functions.
From inside test_work.py, I cannot access those variables, even if I add a second import,
from mymodule.globalconfig import *
Why is this? I wanted to use the same syntax as used in my modules.
thank you!
I am using py2.7 and, to get nice rspec-style outputs and verbose diffs,
pytest --spec -vv
Ref;
1.This answer reminded me I could use another format of import. If there are no other answers I will post my workaround. how to share a variable across modules for all tests in py.test
The import syntax that worked for me was directly importing the nested python file in addition to importing the file under test.
from mymodule.work import *
import mymodule.globalconfig as myconfigs
I assume it's a name clash or import circularity issue, but I could not figure out what the problem was. It took me a while so I wanted to be sure to post the solution for future me and others.

Testing backend code in Django

I am writing an authentication back-end in Django to log only a few users.
It is in a folder called restrictedauthentification/ which is at the root of my Django Project. (I am written it down for a specific project.)
It has two files in it : backend.py and tests.py
In the last file, I have written down some tests for it.
But I can't run them with command ./manage.py test because it isn't an installed app.
Any ideas how I could run them ?
Okay, I found a solution that keep me from turning my backend into a module.
Somthing that I didn't understand and that could help some beginners : In python, a test cannot run itself. It need to be executed by a TestRunner.
Now, one could use the TextTestRunner bundled python that execute the tests and show the results on the standard output, but when testing with django, one need to do one thing before and after the test: calling the function setup_test_environment() and teardown_test_environment().
So I just created a class that inherit from TextTestRunner and redefine its methode run() in order that it execute the two functions provided by Django.
Here it is :
from restrictedauthentification.tests import TestRestrictedAuthentification
from django.test.utils import setup_test_environment, teardown_test_environment
from unittest import TextTestRunner
class DeadSimpleDjangoTestRunner(TextTestRunner):
def run(self, test):
setup_test_environment()
super().run(test)
teardown_test_environment()

run every TestCase inside a module

How can you run tests from all TestCase classes, in a specific module under tests package?
In a Django project, I have split tests.py under tests/
Each file(module) has several TestCase classes, and each of them having several test methods.
init.py imports each of them.
I already know that I can do these:
Run all the test:
./manage.py test myapp
Or run specific TestCase:
./manage.py test myapp.OneOfManyTestCase
Or run very specific test method from a TestCase class:
./manage.py test myapp.OneOfManyTestCase.test_some_small_method
However, I can't figure out how to run every TestCases from a particular module.
Say, OneOfManyTestCase class is from tests/lot_of_test.py, and there are other test cases too.
Django doesn't seem to care about modules with TestCases.
How can I run all the TestCases inside lot_of_test?
I think to achieve this you need to subclass your own TestRunner from DjangoTestSuiteRunner and override build_suite method.
I ended up writing down my own TestSuiteRunner, like #sneawo said.
After Django-style fails, try importing as usual python-style.
One line to fix:
suite.addTest(build_test(label))
into
try:
suite.addTest(django.test.simple.build_test(label))
except ValueError:
# change to python-style package name
head, tail = label.split('.', 1)
full_label = '.'.join([head, django.test.simple.TEST_MODULE, tail])
# load tests
tests = unittest.defaultTestLoader.loadTestsFromName(full_label)
suite.addTests(tests)
and set TEST_RUNNER in settings.py:
TEST_RUNNER='myapp.tests.module_test_suite_runner.ModuleTestSuiteRunner'

How to load custom packages from inside Django apps into views.py?

I am new to Django and have a little problem with making all the project structure clear and organized and so on.. The MVC stuff in Django is quite easy to use when making a small project. However, when I am trying to get a new layer (application logic, like three-tier architecture) between views.py and models.py I have problem to do so.
I have the following structure:
mysite/
manage.py
app1/
models.py
views.py
logic/
__init__.py
Class1.py
parser.py
...
and I want to load into views.py stuff from Class1.py and parser.py.
How do I do it?
Neither of the following works:
from app1.logic import *
from app1.logic.Class1 import Class1
Also, it would help me if somebody could list me some example of a really huge django project. It looks like either lots of classes and .py files should be in every app folder or everything is in the models.py.. Both look a little disorganised and I'm sure there is a way to make it a little bit clearer (like in Zend or Symfony).
And, I'm working with Python3 and Django 1.5b2.
Thanks.
If Class1 or parser import from views, then you have a circular dependency. You'll need to move any shared code out into a third file.
You might consider though whether you need all those separate files under logic. In Python there's no requirement to have a class in its own file, so maybe you could have a single logic.py instead of a logic directory containing several files.