|
| 1 | +# Example Python Project |
| 2 | + |
| 3 | +This is how I like to lay my Python projects out. You don't have to do it |
| 4 | +this way, but this way works for me. If you think it's bad or out of date |
| 5 | +feel free to create an issue or even a pull request. |
| 6 | + |
| 7 | +## To run me |
| 8 | + |
| 9 | + make dev |
| 10 | + |
| 11 | +Then open the project dir in Visual Studio code, if you like to use an IDE. |
| 12 | + |
| 13 | +## Project structure |
| 14 | + |
| 15 | +### Makefile |
| 16 | + |
| 17 | +I like to use a `Makefile` to install my dependencies, build env, run tests |
| 18 | +and create packages. I get tab completion and it doesn't matter what CI |
| 19 | +platform I use, I just run `make stuff` and let GNU Make deal with it. |
| 20 | +`Makefile`s are of course an ancient curse, but if you don't use them you'll |
| 21 | +have to live in interesting times, which is a far worse curse. |
| 22 | + |
| 23 | +The trick is: |
| 24 | + |
| 25 | +1. Have each step run one script that has a good name, so they're documented |
| 26 | + by that script and its name, and can be run without `make` - like from |
| 27 | + your editor or whatever. I keep mine in `./build-scripts/`. |
| 28 | +2. Have the dependencies for the step be the script itself plus one file |
| 29 | + that it creates and anything that it depends on. So when you switch branch |
| 30 | + and the script or its inputs have changed, it'll blow the cache and re-run |
| 31 | + that step and all the ones after it. |
| 32 | +3. If you don't know what the output file will be then just fake one. Like |
| 33 | + when installing `requirements-test.txt` I `touch ./venv/.test-dependencies` |
| 34 | + so it knows that step has been done and won't run it again. |
| 35 | +4. Remember to remove those files in `clean.sh`. Remove everything in there. |
| 36 | +5. Try to avoid use Make's advanced features. In a world where you can do |
| 37 | + anything, what you *don't* do is what defines you. |
| 38 | + |
| 39 | +As a bonus, the file is self-documenting. Type `make help` to see what it can |
| 40 | +do. |
| 41 | + |
| 42 | +### Linting and commit checks |
| 43 | + |
| 44 | +I like `flake8` for linting because it's not as harsh as `pylint`, and is |
| 45 | +supported by my IDE of choice. |
| 46 | + |
| 47 | +Part of the `make dev` setup installs `pre-commit`, which will make sure that |
| 48 | +commits are up to a certain standard. The provided config file runs code |
| 49 | +formatters, linters and a few other checks. Have a look at |
| 50 | +`.pre-commit-config.yaml` to see what's going on in there. |
| 51 | + |
| 52 | +### IDE |
| 53 | + |
| 54 | +I'm using VS Code because it works everywhere and supports all the things that |
| 55 | +I need. I commit my config to source control so anyone can open the project dir |
| 56 | +and start hacking with a working debugger and the tests auto-detected. |
| 57 | + |
| 58 | +The config also provides recommendations for extensions. |
| 59 | + |
| 60 | +### Package layout |
| 61 | + |
| 62 | +Under the `example_project` dir there's a `pyproject.toml` that defines the |
| 63 | +project and its dependencies. Then there's `src` and `test` dirs that contain |
| 64 | +the code and tests. |
| 65 | + |
| 66 | +The layout is as-per the pypi packaging guidelines. When referencing stuff in |
| 67 | +the code I tend to use the full `package.module` names because otherwise imports |
| 68 | +tend to break in weird ways in different environments. |
| 69 | + |
| 70 | +### Testing |
| 71 | + |
| 72 | +I use `pytest` and write tests in a functional style, and develop using TDD. |
| 73 | +If you get into the habit of actually running a new piece of code from a new |
| 74 | +unit test then you'll find yourself writing testable code. Tests are |
| 75 | +fundamentally just other pieces of code, so if a function is difficult to test |
| 76 | +then it's probably difficult to reuse too. |
| 77 | + |
| 78 | +Run `make coverage` for a test coverage report. I like to keep my coverage at |
| 79 | +100%. |
| 80 | + |
| 81 | +## Some opinionated stuff |
| 82 | + |
| 83 | +### Megarepos |
| 84 | + |
| 85 | +When working on microservices and with other teams I prefer a single mega repo |
| 86 | +than lots of different ones if I can help it. Otherwise the style of things |
| 87 | +will diverge, deps will change, things will get forgotten about and turn up |
| 88 | +when you go to add something then add days of work, they'll be too difficult to |
| 89 | +test. Keep everything together and you've got one source of the truth. Your |
| 90 | +stubs, test data, functional and integration tests, infrastructure code, |
| 91 | +architecture diagrams and specs - everything you need to actually understand |
| 92 | +the project in one place. |
| 93 | + |
| 94 | +## Docs in Markdown |
| 95 | + |
| 96 | +Did you know that you can add a `README.md` to any dir and it'll get rendered |
| 97 | +to HTML in most online viewers for git repos? Drop them in wherever you think |
| 98 | +a directory/subtree needs an explanation for existing. |
| 99 | + |
| 100 | +I think it's worth keeping most of the project documentation in Markdown as a |
| 101 | +general rule, up to a point anyway. The project wiki is somewhere else, it goes |
| 102 | +out of sync, it's difficult to see what the docs were on a specific release |
| 103 | +tag. Docs in the codebase can be edited alongside new features and have a much |
| 104 | +richer history. |
0 commit comments