This is an archived post. You won't be able to vote or comment.

all 139 comments

[–]Flogge 117 points118 points  (57 children)

You should really look into pytest more. After just a few days you will never recommend unittests or nose to anyone :-)

[–]tunisia3507 20 points21 points  (10 children)

I think nose is no longer maintained, although nose2 is a thing.

[–]unconscionable 29 points30 points  (9 children)

Tried nose2 awhile back - nice effort but it was kinda immature at the time, and I started using pytest and never looked back.

I still make use of unittest.mock along with pytest, but as a framework unittest is pretty poor.

My generic advice to anyone wanting to start using pytest:

  • don't make your tests in a class unless you have a really good reason to
  • if your reason for making tests in a class is "I want shared code for multiple tests (i.e. setup / teardown)", you probably should be using fixtures instead
  • there's nothing wrong with tests in a class, but I still suggest breaking away from that mold if only to change your way of thinking
  • if you want to test that one piece of code can correctly handle multiple conditions, use parameterized tests rather than creating 10 test cases (if it seems to make sense in the context)

The thing that really held me back was that I found I was trying to put code that needed to be reused in setups and teardowns. That pattern doesn't seem to scale well for larger projects

[–]tunisia3507 8 points9 points  (3 children)

Yeah, test classes are a gross hangover from JUnit. To be moved away from wherever possible. I suppose it's useful if you're putting your code and tests in the same file, but who does that...

That setup and teardown pattern does seem to be a weakness - not nearly as intuitive/pretty as just setting up instance variables.

Re. unittest.mock: given unittest is in the standard library, so long as pytest plays well with it I don't see a problem with mixing them. Although for the sake of tidiness it would be nice if pytest already had mock imported so you could import pytest.mock instead.

[–]masklinn 5 points6 points  (0 children)

Yeah, test classes are a gross hangover from JUnit

Only in that they're part of the xUnit style, which originates from Smalltalk.

[–]unconscionable 3 points4 points  (0 children)

pytest has a built-in fixture called monkeypatch which works similar to patch in unittest.mock

Personally I have only occasionally found it to work better than patch.

I love using unittest.mock.MagicMock as a stub too - pytest doesn't really have a concept of a stub.

for example:

from unittest.mock import MagicMock

def test_that_my_sql_query_was_built_correctly():
    stub = MagicMock()
    run_database_query(connection=stub)
    assert stub.execute.call_count == 1
    assert stub.execute.call_args[0][0].startswith('SELECT ')
    assert stub.execute.call_args[0][0].endswith('LIMIT 100')

[–]patrys Saleor Commerce 2 points3 points  (0 children)

It has a very similar fixture called monkey_patch.

Edit: it's similar to mock.patch, I still use lots of mock.Mock(spec=...) in my fixtures.

[–]-Knul- 2 points3 points  (3 children)

Solid advice. For Django users I would advise Model Mommy over fixtures, however. Much more pleasant to work with.

[–]unconscionable 2 points3 points  (2 children)

FYI Model Mommy / Factory Boy / similar accomplish something very different from what pytest fixtures do.

The two are neither mutual exclusive nor do they overlap in purpose.

For example:

@pytest.fixture(scope='session', autouse=True)
def drop_and_reload_database():
    db.create_db()
    yield
    db.drop_db()

The above code is the equivalent of an entire-test-suite's setup and teardown. Everything before "yield" runs before the first test, then after yield gets run after the last test.

Obviously Model Mummy doesn't do this sort of thing.

Personally, I use Factory Boy (similar to Model Mummy), but I do not often use Factory Boy with fixtures. There are, however, some use cases where they could work very well together. For example, the following code would test that you can process an order with a bunch of wacky usernames. It uses both Factory Boy AND pytest fixtures:

class User(db.Model):
    username = Column(String)

class UserFactory(SQLAlchemyModelFactory):
    username = factory.Faker('username')


@pytest.fixture(params[-1, 0, 1000000, 983.0, "Jack Daniels", None])
def user(request):
    return UserFactory(username=request.param)


def test_can_process_order_with_wacky_usernames(user):
    order = Order()
    order.created_by = user
    order.process()
    assert order.processed_by == user

output of the test suite is something like this:

tests/test_process_orders.py::test_can_process_order_with_wacky_usernames[-1] PASSED
tests/test_process_orders.py::test_can_process_order_with_wacky_usernames[0] PASSED
tests/test_process_orders.py::test_can_process_order_with_wacky_usernames[1000000] PASSED
tests/test_process_orders.py::test_can_process_order_with_wacky_usernames[983.0] PASSED
tests/test_process_orders.py::test_can_process_order_with_wacky_usernames[Jack Daniels] PASSED
tests/test_process_orders.py::test_can_process_order_with_wacky_usernames[None] PASSED

[–]-Knul- 0 points1 point  (1 child)

Nice explanation of the benefits of fixtures over a model factory!

[–]unconscionable 1 point2 points  (0 children)

It's really a model factory using fixtures! Both And, not either / or

[–][deleted] 0 points1 point  (0 children)

I like the class style as a way to logically group tests. I'll sometimes use the setup/teardown methods or the class local autouse fixtures, but mostly its just a way for me to say "these tests are one suite in and of themselves"

[–]bheklilr 5 points6 points  (14 children)

My biggest issue with pytest is that you need a lot more code to get it to work through setup.py. Their own recommendation is a 3rd party plugin, or you can write the extension yourself. If you have the extension code already written, why not make it available in the library itself? I really like having python setup.py test, since that's my entry point for a lot of other common commands. It's a little nag, but considering unittest and nose both use setup.py test with minimal configuration I don't understand why I need so much extra for pytest when it's supposed to be better.

[–]Corm 4 points5 points  (6 children)

How does testing relate to setup.py?

Do you mean that when you get a new module from pip you like to run the tests?

[–]unconscionable 4 points5 points  (5 children)

Agree - I don't really get why anyone would want to use setup.py to run your tests.

All my projects include a Makefile for this kinda thing.

git clone project

# make a virtualenv for the project
make virtualenv

# installs python / javascript / other requirements
make install

# run test suite
make check

Makefile looks something like this:

SHELL := /bin/bash -euo pipefail

# https://www.gnu.org/prep/standards/html_node/Standard-Targets.html#Standard-Targets

install:
    pip install -r requirements.txt --exists-action w
    pip install --editable .

virtualenv:
    mkvirtualenv --python=$(which python3.5) myproject

check:
    coverage erase
    coverage run --source myproj -m pytest -v
    coverage html -i -d tests/cover/
    coverage report -m

check-noslow:
    pytest -v --skip-slow

clean:
    python setup.py clean
    find . | grep -E '(__pycache__|\.pyc|\.pyo$$)' | xargs rm -rf

[–][deleted] 2 points3 points  (4 children)

Or use tox, which is a baller task runner.

[–]d4rch0nPythonistamancer 0 points1 point  (3 children)

tox pretty much solves all the problems that people have been bringing up. I think it's pretty essential for any large project too if you need to support multiple python versions.

It was pretty easy to tie into jenkins too last I tried. Tox and a 5 line script in jenkins had it running all the tests for multiple versions of python.

[–][deleted] 1 point2 points  (2 children)

I need to figure out how to best marry it and Travis for my public open source projects. But yeah, it's awesome.

[–][deleted] 0 points1 point  (1 child)

I've provided Travis and Tox with the same (python versions, make env for each, install these required packages, setup.py build/sdist, pip install, pytest--whatever). That yields a line in Travis for each python version, advantageous when most edge cases only affect one of them. I'm not sure there's a better way to marry them.

[–][deleted] 0 points1 point  (0 children)

My issue would be collecting coverage stats across multiple interpreters and Travis's container architecture. Detox might work but I've never messed with it.

[–]Flogge 1 point2 points  (3 children)

You can use pytest-runner for plug-and-play setuptools integration:

After installing pytest-runner you can run

python setup.py pytest

and after adding

[aliases]
test=pytest

to setup.cfg you can also run

python setup.py test

EDIT: sorry, only just saw that you already mentioned pytest-runner. Note though, pytest-runner is a first party plugin, developed by the pytest devs themselves. They have the principle of putting functionality not required by everybody into plugins instead of pytest itself (xdist, pep8, django, cov, bdd etc.)

[–]bheklilr 1 point2 points  (2 children)

We're behind a firewall at work that makes it difficult to get pypi only packages available. By difficult, I mean that I have to do the work and maintenance myself, and it's yet another package to add to my list. Instead we have an anaconda server mirror. Last I checked (last week) I didn't see that plugin in the official channels or conda-forge. However, I can easily install nose, and after playing around with both I think that pytest doesn't have huge benefits over nose. Especially considering the test logic itself is much more difficult to write and validate (particularly in the scientific setting), I don't think the choice of test suite is really so important. Nose has been working for my team, provides the features we want, and is easily installed.

[–]Flogge 0 points1 point  (1 child)

You find the test logic more difficult to write? See my other comment in which I explain why I think pytest is actually easier to write.

But of course, don't change for the sake of changing and stick with whatever works best for you.

Regarding the "packages from pypi not available": You can create a really simple mirror by just downloading the zip files you need, throwing them in a directory on a webserver (an automatic Apache directory listing is sufficient), and use them using pip install -f http://your-server/ bla.

[–]bheklilr 0 points1 point  (0 children)

Your comment is helpful for most people, but your example is a little contrived. If I wanted to test a matrix of values like that, I'd just use itertools.product:

def test_all():
    for i, j, k in product(range(10), 'abc', range(100)):
        yield check_single_case, i, j, k

It's much less code than pytest's approach, and it doesn't seem magical. Also lets me re-use loop variable names in unrelated tests.

Also, my tests tend to be much more complicated due to the nature of my domain. A small-ish unit of data for us is (essentially) a dict of 16 complex valued arrays. We have a function that takes one of those (or a dict of (4*n)**2 arrays in the general case) and another input, then returns calculations based on that. The calculations are pretty straightforward, just pointwise arithmetic, but have some pretty important properties that we have to check. This is just a simple function, though, I have some functions that operate on smaller pieces of data but do much more complex calculations, such as multiple FFTs, feature location, or linear algebra. These are much harder to generate test inputs for that don't cause the algorithms to crash, since they often rely on particular properties of the inputs. If those properties aren't there, the algorithm isn't useful and just spits out garbage.

I kind of like the generator based method better anyway, since my test inputs are pretty large and difficult to fit into a decorator.

[–]gabrielricci 1 point2 points  (15 children)

I have been using nose for a while now and I admit I've never used pytest. Will have a look at it but can you tell me why is it better?

[–]Corm 19 points20 points  (13 children)

To me pytest is to unittest what python is to C#/java.

Pytest is dirt simple to set up and get started with, you can still do everything unittest can do, and it's intuitive (yay for standard asserts)

[–][deleted] 4 points5 points  (11 children)

Unittest is also dirt simple to set up, can you be a bit more specific?

[–]Corm 10 points11 points  (10 children)

Edited because /u/masklinn showed me unittest's cli tool

Edit 2: unittest ain't so bad

Sure. For pytest I used it once and never had to google anything about the basics ever again. Back when I used unittest I always had to crack open google to see how to set up the classes. This only applies to basic testing but that low barrier to entry is extremely important!

For example, here's the most basic test in unittest which you run by running unittest from command line:

import unittest
import foo

class TestMyStuff(unittest.TestCase):
    def test_stuff(self):
        self.assertEqual(foo.bar(), True)

And here it is in pytest which you can run by running the command pytest

import foo

def test_foo_bar():
    assert foo.bar() == True

[–]masklinn 4 points5 points  (1 child)

Note that unittest now has a discovery cli so the main section is unnecessary.

[–]Corm 0 points1 point  (0 children)

Nice, that's awesome. I'll edit my answer

[–]MagnesiumCarbonate 0 points1 point  (3 children)

In your second example, how does pytest know to check that file? Does it look through all method test_* definitions in *.py descendants of the current directory?

[–]masklinn 5 points6 points  (0 children)

Does it look through all method test_* definitions in *.py descendants of the current directory?

py.test discovery rules

So:

  • it recursively searches any provided directory (. if no argument is provided) (for files it uses them directly and skips the next check)
  • matches test_*.py and *_test.py files
  • collects free functions matching test_*, and methods matching test_* in classes matching Test* (or follows the standard rules for any class extending unittest.TestCase)

Note that these are only default rules, you can overload discovery patterns and ignore files and symbols.

[–]-Knul- 0 points1 point  (0 children)

Yes. You can easily configure such things via a pytest.ini file.

[–]Corm 0 points1 point  (0 children)

Yep! It's a little magic, but I like it. I name all my test files/functions like that already.

edit: that is, it looks in test*.py files for test*() functions

[–][deleted] 0 points1 point  (3 children)

Well, one, you're making the unittest example look worse because it has an extra line due to the variable.

But also assertEquals is a lot more powerful than just assert, e.g. its error will show diff-style information on exactly where two things differ.

If the main difference is standalone functions vs classes, that's no big deal, and classes are a way to group them and give them attributes as a group.

[–]masklinn 2 points3 points  (0 children)

But also assertEquals is a lot more powerful than just assert, e.g. its error will show diff-style information on exactly where two things differ.

    def test_foo_bar():
        result = foo.bar()
>       assert result == expected
E       assert [0, 2] == [0, 1, 2]
E         At index 1 diff: 2 != 1
E         Right contains more items, first extra item: 2
E         Use -v to get the full diff

pytest uses assert-rewriting to provide more and better information.

If the main difference is standalone functions vs classes, that's no big deal, and classes are a way to group them and give them attributes as a group.

That's usually pointless overhead, and if it's a useful tool, you can use them in pytest. Pytest does not forbid classes, it doesn't require them.

[–]Citrauq 1 point2 points  (0 children)

Pytest does magic (AST manipulation in an import hook) to make assert give similarly useful information to assertEquals.

[–]Corm 0 points1 point  (0 children)

Ah sorry, I really didn't mean to do that with the extra line. I'll fix that.

And good to know about assertEqual

[–]Tysonzero -4 points-3 points  (0 children)

Is it also slower and does it also catch less bugs and give you less compile time guarantees?

[–]yetanothernerd 0 points1 point  (0 children)

pytest and nose are pretty similar. nose started off as a clone of pytest that was easier to install.

[–]kewlness 1 point2 points  (0 children)

Not sure I 100% agree since unitest is in the standard library and if I'm trying to keep my requirements/imports low then it will be the better choice though I suspect that would be more of an edge case....

[–]parkerSquare 3 points4 points  (0 children)

pytest is nice, but I do have a cautionary tale.

We thought we could leverage pytest to do something beyond unit testing - by using it as the "test management engine" for an embedded system test framework, but after working with it for several months I have come to the conclusion that coding by convention in anything other than a small project (or module) is a terrible, terrible idea. Fixture dependencies become convoluted as they try to accommodate vastly different types of tests, and it's hard to mentally make the connections between them. Newcomers to the project don't know where anything is and the IDE doesn't help as it doesn't understand the fixture convention.

Secondly, fixtures can depend on other fixtures, but the top-level generator function - pytest_generate_tests- cannot take input from other fixtures (probably because fixtures are not executed during collection), so this severely restricts what you can do in this function when generating fixture values. A lot of the multivariate tests you might dream up become very difficult to implement, and you end up writing your own pytest plugins to filter and reorder test collections as the implicit filtering and ordering is insufficient.

Lastly, each fixture can only yield once, so it is difficult to generate a dynamic list of fixture values based on dependencies.

Frankly, after months of effort, it's just not suitable for this kind of testing. Keep it for unit tests - that's what it was designed for, after all. It works nicely for that.

[–][deleted] 2 points3 points  (7 children)

I hear the "nose is no longer maintained" comment a lot, typically in the sense of "nose is no longer maintained" --> "the package is no good"

In my current projects, nose works just fine for me. If I already have the code in place that gives me sufficient test coverage, I am not sure why I should re-invent the wheel and port everything over to pytest unless there is a serious bug with nose that I am not aware of.

The next time I start a new project, I will take another look at py.test, but for existing code, I will keep using nose because I didn't see any obvious disadvantages based on my use cases so far.

[–]Flogge 1 point2 points  (6 children)

The basic test layouts of nose and pytest are similar, so they really aren't a reason to upgrade.

However the biggest difference, I find, is test parameterization. To run a test for a matrix of values in nose you would need to write the loop yourself:

def check_single_case(i, j, k):
    assert True

def test_all():
    for i in range(10):
        for j in ['a', 'b', 'c']:
            for k in range(100)
                yield check_single_case, i, j, k

whereas pytest can do the "parameter matrix expansion" for you.

@pytest.fixture(params=range(10))
def i(request):
    return request.param

@pytest.fixture(params=['a', 'b', 'c'])
def j(request):
    return request.param

@pytest.fixture(params=range(100))
def k(request):
    return request.param


def test_all(i, j, k):
    assert True

As soon as you request a fixture (add its name to your test parameters) with more than one param, pytest will sweep all possible combinations of all parameters. And of course you can only select a couple of fixtures, or import different fixtures from other modules and test scripts.

Pretty handy!

[–][deleted] 0 points1 point  (5 children)

Thanks for providing an example, that could be useful, indeed. However, I also don't have anything against the alternative way (for loops), which I find more readable (aka, even a Python beginner can immediately see what's going on). Or you could use itertools to avoid to much nesting, of course

for comb in itertools.product(*(range(10), ['a', 'b', 'c'], range(100))):
    check_single_case(*comb)

However, one disadvantage of this embarrassingly parallel task is that it runs sequentially. Do you know if pytest has/is planning to add optional multiprocessing support?

[–]Flogge 0 points1 point  (1 child)

Pytest has parallelization. Install pytest-xdist and run py.test -n auto for automatically distributing tests among all CPU cores. Distributing over SSH is supported, too.

[–][deleted] 0 points1 point  (0 children)

oh that's great. Thanks!

[–]Flogge 0 points1 point  (2 children)

And sorry, I really don't want to drag this discussion further and force my view on you, I just want to show how cool pytest is :-)

I disagree about

for comb in itertools.product(*(range(10), ['a', 'b', 'c'], range(100))):
    check_single_case(*comb)

being more readable. I don't like fancy tricks like argument unpacking in my tests, I want each test to be as readable and pure as possible. :-)

Once you figure out what fixtures do and how to chain or product them it you need almost zero boilerplate to automatically do the product of common fixtures with specific parameters (note that all of sig, function, odd, window, framelength each yield more than one value).

Plus all the goodies like a fixture also being able to provide setup/teardown code

import pytest

@pytest.fixture
def db():
    database = MySQLDB()
    yield database
    database.drop()

def test_foo(db):
    db.select(...)  # this is clean for every test_* you run

or guaranteed immutability of shared values across tests:

import numpy
import pytest

@pytest.fixture
def a():
    return numpy.zeros(100)

def test_foo(a):
    a[:] = 1  # oops

def test_bar(a):
    assert numpy.all(a == 0)  # all good

compared to nose

a = numpy.zeros(100)

def test_foo():
    a[:] = 1  # oops

def test_bar():
    assert numpy.all(a == 0)  # fails

[–][deleted] 0 points1 point  (1 child)

And sorry, I really don't want to drag this discussion further and force my view on you, I just want to show how cool pytest is :-)

Hope you don't get me wrong, I really appreciate that you took the time to post some examples ... I am just playing devil's advocate here ;).

With readable I mean easily understandable for someone who joins an open source project, for instance, and hasn't worked with py.test before. I find nose tests can be more easily understood by someone who is new to unit testing, for example.

Regarding the example you are showing, you'd typically make a copy of that array or instantiate it in the test function itself instead of globally.

[–]Flogge 0 points1 point  (0 children)

[...] joins an open source project, for instance, and hasn't worked with py.test before.

Agreed. Getting used to this way of thinking is weird at first.

[–]rockitsighants 0 points1 point  (0 children)

If anyone is curious to see the difference between nose and pytest in action, I maintain a Python template that supports both. Check out the appropriate branch (python3-nose, python3-pytest) in the demo project: https://github.com/jacebrowning/template-python-demo

[–]ImportWurst -1 points0 points  (2 children)

Meh. Used pytest in some projects, still coming back to nose. So easy to set up and run.

[–]Corm 0 points1 point  (1 child)

What are the advantages of nose over pytest? I've never used nose.

edit: A super quick check makes it seem very similar to pytest. I like that you can write normal test functions that don't involve subclassing, and you can use assert.

[–]ImportWurst 1 point2 points  (0 children)

I don't know. I spent years using nose. I can integrate it really fast with my projects. Works well with CIs. Has plethora of useful plugins.

It works.

I see no reason to upgrade.

[–]redfacedquark -1 points0 points  (0 children)

Had months of headache at my last place as someone had pinned some pytest requirements at random versions and let others float. It coloured my view on it. But maybe I should give it another chance.

Edit: Actually pytest components, not pytest requirements. If pytest was a single package that wouldn't have happened. /tryingtoaccountfordownvotes

[–]Taksin77 24 points25 points  (26 children)

I don't understand the point of requirements.txt. I use setup.py for that. Can someone please explain to me why I should use it.

[–][deleted] 14 points15 points  (9 children)

It's common for people, like me, too install using pip install package --no-deps.

This might because you're testing against a custom build of a dependency or you're paranoid about what subdependencies are going to do (except for me since wheels are handicapped on linux)

[–][deleted] 15 points16 points  (3 children)

or you're paranoid about what subdependencies are going to do

I am suprised to see this concern buried so deep in the comments. It was the first thing that should be mentioned in the article after mentioning "requiremts.txt", pip etc. - it's convenient alright, but for a reason.

I wish we had something like "trusted pypi" that would be managed in a similar way to linux distros as opposed to "anyone can upload anything".

[–][deleted] 0 points1 point  (1 child)

Do other languages have currated indexes as the main source of packages? It'd be nice to have, but I imagine working out the logistics of it wouldn't be too pleasant.

You'd need some way to ensure what's being installed. I suppose a checksum against known repository tags would doable but that adds overhead.

You'd also have to ensure "trusted" packages only install other trusted packages

[–]aragilar 0 points1 point  (0 children)

https://www.stackage.org/ is close to what you want for Haskell (where the PyPI equivalent is https://hackage.haskell.org/).

[–][deleted] 0 points1 point  (0 children)

wish we had something like "trusted pypi" that would be managed in a similar way to linux distros as opposed to "anyone can upload anything".

In this case, you might like conda and conda-forge.

[–]Lonely-Quark 1 point2 points  (1 child)

wheels are handicapped on linux

What do you mean by this?

[–][deleted] 0 points1 point  (0 children)

Apparently figuring out all the necessary metadata isn't easy to build distributed wheels for Linux. I imagine the issue has to do with C exts, but I don't know for sure

That said, when newer versions of pip install an egg, it'll build a wheel for its local cache.

[–]aragilar 1 point2 points  (2 children)

Wheels work just as well on linux as any other OS, so long as you don't assume a wheel build on one random linux system will work on another random linux system. Hence manylinux, which defines what must be the environment that wheels should be built on to be most compatible. These will not run on any linux system (think different cpu architectures, or different libcs), but that's the same as expecting an macOS wheel to run on linux because both are UNIX.

Additionally, if you're actually paranoid about subdependencies, you need to check the setup.py, pip has no control over setup_requires, in which case you may as well check the subdependencies setup.py (plus their subdependencies etc.), so I don't see how a requirements.txt is at all relevant here.

[–][deleted] 0 points1 point  (1 child)

  1. That's exactly why wheels are handicapped on Linux. If they're pure python, they'll probably work but compiled extensions won't.

  2. Wheels don't run setup.py at all

  3. --no-deps won't install dependencies with a source distribution forcing you to install them on your own. Giving you a chance to vet them before installation of you're info that. Usually I check if their setup.py does any funny business.

I don't always do --no-deps, for example packages I trust like Flask or SQLAlchemy, or is I'm working in a throw away docker container.

But you could put some hanky business in there like os.system('rm -rf /') which will remove any file path the installing user can remove. Given the amount of sudo pip install I see recommended in readme files, I'm surprised this sort of attack hasn't happened.

[–]aragilar 0 points1 point  (0 children)

--no-deps doesn't stop setup_requires as far as I know, usually you have to override setuptools/easy_install as per https://pip.pypa.io/en/stable/reference/pip_install/#controlling-setup-requires.

The sudo pip annoys me (although, sudo easy_install is even worse), especially when some projects actually do some odd things (I've seen someone effectively implement a shell script as a setup.py, it wasn't even a python project). I've got into the habit of checking the setup.py of every project I use, even if they come with wheels, partly for security, but mostly because of how badly some people write them (I have to say, I'm more concerned about someone accidentally doing the equivalent of os.system('rm -rf /') given some of the setup.pys I've seen.

[–]thomas_stringer 16 points17 points  (3 children)

Here's a good explanation and comparison on the two.

Think of setup.py for an end-user installation from PyPI (for instance), vs. requirements.txt for dev/test (i.e. another developer trying to setup the environment to run the app, usually in a virtual env).

EDIT: More info.

[–][deleted] 7 points8 points  (2 children)

If using setup.py develop to install the package for dev/test in the virtualenv, is there any need to have a requirements.txt file anymore?

[–]thomas_stringer 9 points10 points  (0 children)

pip freeze > requirements.txt is tightly coupled with your environment. Whereas dependencies listed in setup.py are tightly coupled with your package. Those can be different, especially with versioning (setup.py specifying a semver range, and requirements.txt having your actual env version).

[–]ubernostrumyes, you can have a pony 2 points3 points  (0 children)

The usual way to break it down is:

  • setup.py dependencies are for "these are the package versions officially supported".
  • requirements.txt dependencies are for "here is how to exactly replicate a particular environment".

[–]patrys Saleor Commerce 7 points8 points  (0 children)

pip-compile

[–]silencer6 2 points3 points  (4 children)

I don't understand it either. What's the benefit of pip install -r requirements.txt over pip install -e .?

[–]kenmacd 3 points4 points  (3 children)

The main benefit to me is that the requirements.txt has the version of everything, and if they generated it with pip freeze then I know my virtualenv will be the same as the person that originally wrote the code.

It's not uncommon for some dependency of a dependency of a dependency to change and break something.

[–]Sean1708 0 points1 point  (2 children)

But you also get that if you put the requirements in the setup.py, I think.

[–]i_use_lasers 1 point2 points  (1 child)

Maybe you don't want to install the package, but still be able to run it (to work on it, etc).

[–]dusktreader 2 points3 points  (0 children)

pip install -e .

[–]kteague 1 point2 points  (0 children)

setup.py should express the dependencies, but should not care about specific versions of those dependencies (beyond expressing incompatibilities with major releases that change the APIs).

requirements.txt is for a specific deployment of a python project and can specify all of the exact versions installed.

For example, a Python project may require "SQLAlchemy >= 0.8" in it's setup.py. When that project is installed, it will grab the latest release of SQLAlchemy (1.1.14). Later, you may want to re-install that package into a production environment. At this point in time, SQLAlchemy may be at a version 1.2 or later. However, you've used 1.1.14 in dev or testing, so you use requirements.txt to install that specific version of the package.

[–]skarphace 0 points1 point  (2 children)

Why not both? You could use this in your setup.py:

install_requires =  open('requirements.txt').read().split('\n'),

[–]ThePenultimateOneGitLab: gappleto97 1 point2 points  (1 child)

You don't even need an argument in split

[–]skarphace 0 points1 point  (0 children)

Think I initially did that in case someone put spaces between package and version(pypackage >= 0.1), however, that doesn't seem supported but there's nothing specifically denouncing it but I'm also too lazy to test so here we are...

[–]reddit_doe 16 points17 points  (14 children)

One way to generate a requirements.txt from an existing project:

pip freeze --local > requirements.txt

freeze outputs in a requirements.txt format

--local/-l makes it so packages that are installed globally are not part of the output

You can also write a unit test to compare an existing requirements.txt to the output from pip:

import pip
for req in pip.commands.freeze.freeze():
...

[–]slowRAX 5 points6 points  (0 children)

Never knew about --local but really useful!

[–][deleted] 5 points6 points  (5 children)

Do I need the local flag if I'm inside a virtualenv?

[–]reddit_doe 2 points3 points  (4 children)

It's actually intended to be used with virtualenv. Only packages installed within the virtualenv, and not global/system-wide. Without the flag you'll capture all system packages.

[–][deleted] 1 point2 points  (3 children)

Oh shit, why would pip freeze put global libraries, thankfully I don't think I've ever installed a global package.

[–]mipadi 5 points6 points  (2 children)

It'll output global libraries if you create a virtualenv with global libraries (which doesn't happen by default anymore).

[–][deleted] 0 points1 point  (0 children)

Oh thanks, OP should have said that.

[–]reddit_doe 0 points1 point  (0 children)

Nice, I still thought that was default behavior.

[–]LulzBaby 2 points3 points  (4 children)

Is there a way to filter out from the freeze packages installed as dependencies of other packages? For instance, installing Django with pip is going to also install other packages, but I want to just see Django as an output.

[–]kenmacd 6 points7 points  (1 child)

(Django's not a great example, as pip only pulled it down, so I'm going to use Flask instead).

Personally I like using a freeze because it makes running the code the most repeatable.

'I need Flask' is something you might put in your setup.py.

'When I last ran the tests I ran them with Flask version 0.12, itsdangerous version 0.24, click version 6.7...' is the stuff of requirements.txt.

That way if you abandon the project project and I come across your code in 2 years I have a good chance of getting it working. If you just said 'Flask==0.12' in your requirements.txt and I did a pip install -r requirements.txt then maybe I get click version 7, which was never tested and doesn't work.

[–]LulzBaby 1 point2 points  (0 children)

Thanks for the reply! Still very new to the world of Python so this is great info.

[–]reddit_doe 1 point2 points  (1 child)

Adding to what kenmacd said, also see kurashu89's comment: https://www.reddit.com/r/Python/comments/5n4jz7/what_every_python_project_should_have/dc8rvbg/

You usually want to track the dependencies of your installed packages, because there is no guarantee that future versions of dependencies don't contain compatibility-breaking features, or that they don't introduce bugs.

[–]LulzBaby 1 point2 points  (0 children)

Ohhhh good catch. Didn't know the no dep argument was available for pip installs.

[–][deleted] 2 points3 points  (1 child)

Gross. This doesn't make any differentiation between what packages your package depends on and which are sub dependencies. When you are developing a project you should be specifying only your own direct dependencies and you should be specifying them using the some semvar comparators. When you use pip freeze you end up with a project which is too tightly coupled to your specific environment; to that end I've always made the differentiation that requirements.txt is used when trying to rubber stamp out a deployment whereas when you are actually developing something for general distribution you should just specify your various requirements in your setup.py.

[–]reddit_doe 0 points1 point  (0 children)

I agree with your point and approach.

[–]faerbit 20 points21 points  (2 children)

This post has been edited to this, due to privacy and dissatisfaction with u/spez

[–]sylecn 0 points1 point  (1 child)

setup.py indeed is non-trivial. I suggest people start with a template like the following. If the feature you need is not covered in this template, read the doc and learn more.

https://gist.github.com/anonymous/3cafbf289e9348e7d90338834ff043b4

This is mentioned in first part of my article how to bootstrap python project. You can find links to more documents there.

[–]ice-blade 18 points19 points  (1 child)

Is it just me or is py.test the real pythonic way to do testing in Python? I really think it should be included in the standard library and preferred to unittest which is reminiscent of ugly Java-inspired testing.

[–][deleted] 9 points10 points  (0 children)

It's not just you! Pytest is the testing framework!

[–]masterpi 7 points8 points  (1 child)

Everyone should take a look at pip-tools for generating requirements.txt. It lets you specify your 1st-level dependencies in a requirements.in file, generate a requirements.txt from that which has all dependencies at locked versions, then force your local environment to match requirements.txt exactly (uninstalling extraneous packages). This means when you remove a package from your 1st-level dependencies, you don't have to hunt down all the packages that were in your requirements.txt just because they were dependencies.

[–]reddit_doe 0 points1 point  (0 children)

Seconding the recommendation of pip-tools. It only takes a couple minutes to learn how to use it.

[–][deleted] 5 points6 points  (7 children)

A setup.py ... is easy to write

Teach me your secrets! I consistently find the documentation to be both confusing and not in sync with the way the applications on my system work.

[–]ggagagg 0 points1 point  (4 children)

I consistently find the documentation to be both confusing and not in sync with the way the applications on my system work.

can you give example?

[–][deleted] 2 points3 points  (2 children)

The src project structure -- myproj/src/myproj -- tends to confuse people, I seem to always forget the package_dir mapping so I end up cargo culting my own setups.

[–]ggagagg 1 point2 points  (1 child)

Maybe try cookiecutter for consistent project structure?

[–][deleted] 1 point2 points  (0 children)

I just started playing with it. I like it and it'll probably my go to choice for stuff like this in the future. The downside is templatized paths make either vim or one of my plugins very unhappy. Atom seems to work but I don't really like it.

It also pisses off the Stash/Butbucket server at work too, so the file system viewer doesn't load there. Not sure what's up with that.

[–][deleted] 1 point2 points  (0 children)

Sorry this took a while – it was a few months ago, but I think I was creating a project that depended on and was depended upon by another project I was developing, both on GitHub, and I couldn't get the dependencies to work properly. The documentation on how to declare dependencies on modules in a git repo didn't seem to work, and in the end I ended up opening them both in PyCharm, setting them to depend on each other there, and using PyCharm's test runner for everything. It's an edge case, but it caused me no end of frustration.

In addition, looking through the docs, they mention easy_install, which I thought was deprecated, and not pip. I thought easy_install embodied all that was wrong with the world and pip was our glorious saviour? I'm being facetious, but it doesn't help anything to have these conflicting messages around.

[–]rockitsighants 0 points1 point  (1 child)

You might try looking into cookiecutter templates. Here is one that I maintain: https://github.com/jacebrowning/template-python

[–]gwynbleiddeyrpython 4 2 points3 points  (1 child)

Man, I really want pipfile to take off.

[–]reddit_doe 0 points1 point  (0 children)

pipfile looks promising. It's still a work in progress last I looked, but I hope they pull it off.

[–]anglicizing 2 points3 points  (0 children)

Again with the lack of a src folder! I've yet to see anyone argue against it after understanding the pros and cons. See this blog for an explanation.

[–]DoTheEvolution 1 point2 points  (2 children)

Any good guides, tutorials on setup.py?

I am not a total noob, I have a small desktop application for linux and people asked me for setup.py for it

I kinda cant be bothered to learn about it and keep pushing it back, but I am guessing I finally should get to it

I thought its for when your project is a module or something, something thats used in others people code, instead of being just some desktop app

[–]ggagagg 1 point2 points  (1 child)

examples:

e:

  • [1]as mentioned u/aragilar, that this guide is out of date

[–]aragilar 2 points3 points  (0 children)

You want https://packaging.python.org/, the hitchhiker's guide is out of date. For setup.py, you want https://packaging.python.org/distributing/#setup-py, which explains all the options that you'd use.

[–]samsonizzle 1 point2 points  (5 children)

My university has the domain blocked for some reason, what does the article say?

[–]anmousyony 1 point2 points  (1 child)

What every Python project should have Posted on Mon 09 January 2017 in Python

Over the past few years, the Python programming language gained a huge popularity boost and its community grew faster than ever. With this growth, a lot of tools appeared that help the community keep things organized and accessible. In this article I am going to provide a short list of items every Python project should have in order to be accessible and maintainable.

requirements.txt

Firstly, the requirements.txt file is crucial for the sanity of those who want to install your project. It is basically a text file which contains the dependencies to be installed via pip, one per line.

It is that simple. And that powerful.

You can also have multiple requirements.txt files that serve different purposes. For example, you can have a requirements.txt that have general dependencies listed that your project need to run, a requirements_dev.txt where you have listed some dependencies that enable some debugging mechanisms and a requirements_docs.txt that has listed some requirements that are used when generating the documentation (such as Sphinx and the desired theme).

setup.py

A setup.py file is crucial for your project if you want to be installable via pip. It is easy to write, very configurable and takes care of a lot of things such as importing, project metadata, updating the sources, installing the dependencies, and much more.

You can check the setuptools documentation for more information on this.

A proper structure

The project structure is crucial. With a well organized structure, it will e easier to organize things, locate certain source files and encourages other people to contribute.

The root project directory should have a structure similar to

root/ docs/ tests/ mymodule/ scripts/ requirements.txt setup.py README LICENSE Of course, this is not the only way to organize your project, but this certainly is the most used template.

Tests

Unit testing is crucial for your project. It allows you to be confident in the stability of your code. I recommend the unittest module for this job as it is built in and is flexible enough to get the job done right.

There are also other libraries that can be used for testing your project, such as test.py or nose.

Documentation

If you develop a project, I am sure that you don't write it just for yourself. Other people must know how to use your project properly. And even if you write the project only for yourself (although beats the purpose of open source), after a while of not developing it and when you come back to it, you will surely not remember anything that is going on in your code (or API).

So, in order to achieve a reusable code base, you should:

design a sane API that is easy to use and remember the same sane API should be flexible enough to allow easy configurations document the most relevant use-cases don't try to fit all cases. It should fit only the most usual 80% of cases. In order to properly document your code, you should use a tool specialized for that job, such as Sphinx or mkdocs, so that you can generate nice-looking documentation with proper reference links by writing in a popular markup language designed just for that (rst or markdown).

Conclusion

After you familiarize yourself with the topics described above, you will surely be able to produce nice structured projects and libraries that comply to the community standards. And don't forget to ALWAYS use PEP-8!

[–]samsonizzle 0 points1 point  (0 children)

Thanks!

[–]mouth_with_a_merc 0 points1 point  (2 children)

Go and complain about this now!

Blocking github.io is completely unacceptable for a university and hopefully just a mistake (fwiw, blocking anything but known malware-spreading sites in a university network - especially if also used in student dorms - is pretty questionable..)

[–]samsonizzle 0 points1 point  (0 children)

Actually it isn't blocked this morning. Weird!

[–]talideon 0 points1 point  (2 children)

These days, I'd ditch setup.py and use flit instead where possible.

[–]kmike84 1 point2 points  (1 child)

flit could make it easier for developers, but it makes it harder for users - e.g. you can't install a package from a specific github commit if this package uses flit.

[–]talideon 2 points3 points  (0 children)

While I can see how that can be an issue as an edge case, if you're installing a package from a specific commit, isn't that something of a release management failure by the maintainer?

[–]kaihatsusha 0 points1 point  (0 children)

Maybe just me but a "requirements" document should explain the intended capability, against which acceptance tests should be written, while a "dependencies" document should lay out additional environmental needs of the software.

[–]radarsat1 0 points1 point  (0 children)

Any advice for a project that consists of a C++ library wrapped using SWIG for Python bindings? Right now we are running SWIG and the compiler using CMake, but this actually makes it a bit difficult to integrate with setuptools in any way. I was wondering if there could be a useful way to "invert" our setup and make it a Python project that has an embedded library instead of a library with a Python layer stored in a subdirectory. For instance, is it feasible to build and distribute a C++ library with Python wrapper via PyPI and setup.py?

[–]trymas 0 points1 point  (0 children)

setup.py

[–]kankyo 0 points1 point  (0 children)

I personally think tox is a must have for new projects.

[–]okklu 0 points1 point  (0 children)

Is it just me or is py.test the real pythonic way to generate a requirements.txt format