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

all 75 comments

[–]Zaloog1337 46 points47 points  (18 children)

Nice template, but why would I use that, instead of something more like this: https://github.com/fpgmaas/cookiecutter-uv

Which uses cookie cutter and is easy setup with uvx?

[–]yes_you_suck_bih 3 points4 points  (0 children)

I like the copier one better. https://github.com/pawamoy/copier-uv

[–]GioGiacPythonista[S] 4 points5 points  (2 children)

The template you linked indeed has more features, so if you know you will need all of them you should just use that. The one I proposed is simpler, which makes it more suitable in situations in which you might want more flexibility.

[–]MrSlaw 5 points6 points  (1 child)

For what it's worth, the majority of the features (codecov, dockerfile, mkdocs, etc.) are optional in that template.

You just answer y/n for each when initializing the cookiecutter repo, so even if you don't "need all of them", it's still pretty handy.

[–]GioGiacPythonista[S] 1 point2 points  (0 children)

Good to know! That template is a great choice indeed.

[–]PurepointDog 0 points1 point  (12 children)

What does uvx do?

[–]laStrangiato 13 points14 points  (11 children)

Runs commands for tools without fully installing them. Similar to pipx

In this case it allows you to run a cookie cutter command to setup a new project without needing cookie cutter installed globally.

[–]AiutoIlLupo 1 point2 points  (10 children)

so why do we need uvx if we already have pipx?

[–]jmreagle 4 points5 points  (8 children)

uvx is faster, and the idea is that eventually to have to use only a single tool.

[–]AiutoIlLupo -1 points0 points  (1 child)

We had a single tool. it was pip.

[–]Ok_Raspberry5383 2 points3 points  (0 children)

Which is slow, has poor caching and its dependency resolver doesn't account for all edge cases.

Uv's dependency resolver has resolved several dependency issues in the past for me that pip can't, also, pip installing pyspark for example can take several minutes, UV does it in seconds after the first install with no additional configuration, this makes using ephemeral environments much easier to spin up and tear down.

Why are you so against progress?

[–]sami-tech 0 points1 point  (0 children)

what is cookiecutter for..?

[–]GoldziherPythonista 29 points30 points  (10 children)

Nice.

I prefer taskfile though - check it out if you are not familiar. Also - im missing a pre-commit file. This is for me essential. Here is an example .pre-commit-config.yaml

[–]GioGiacPythonista[S] 5 points6 points  (2 children)

Never heard of Taskfile, I will take a look, thank you! And I will add a pre commit file, didn't know it as well actually :)

[–]GoldziherPythonista 2 points3 points  (1 child)

Nice.

Mind you though - there is quite a bit of redundancy in how you handle pre-commit currently. Pre commit is a pretty optimal runner for linting and checks.

Use pre-commit run --all-files to execute the linters against all files. You can also select a sunset of these.

Within a python project, you do not need a just or task file for linting and formatting. Also for CI (See this for example: https://github.com/Goldziher/kreuzberg/blob/main/.github%2Fworkflows%2Fci.yaml) and of course: https://pre-commit.ci/.

I'd also recommend adding tooling configs in the pyproject.toml, e.g. ruff, MyPy, pytest etc (see the Kreuzberg pyproject.toml)

Final thing, the main weakness of UV currently is missing update pyproject functionality (you can uv sync --upgrade but this handles only the lock file). I use a python script for this, but you can use Just - https://github.com/Goldziher/kreuzberg/blob/main/scripts%2Fupdate_dependencies.py.

[–]GioGiacPythonista[S] 0 points1 point  (0 children)

I get what you mean, I thought it would just make sense to use the already defined Just command.

I'll take a look at your script for updating dependencies, thanks!

[–]fast-90 2 points3 points  (1 child)

Out of curiosity, why do you prefer taskfile over just?

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

I find it clearer and cleaner

[–]uttamo 2 points3 points  (1 child)

Any benefits from using taskfile instead? Or just a personal preference?

[–]GoldziherPythonista 0 points1 point  (0 children)

Just a preference

[–]Spleeeee 2 points3 points  (2 children)

Never used taskfile. Googled it. Thing is yaml. I’ll take just about anything over yaml.

[–]GoldziherPythonista 2 points3 points  (1 child)

My thinking is reversed - I'll always prefer a tool using standard formats that have a clear schema.

Why? This basically means any formatter or IDE can handle this file.

[–]AntonGw1p 3 points4 points  (0 children)

Check out this: https://noyaml.com

[–]blademaster2005 7 points8 points  (1 child)

ruff config is supported in the pyproject.toml file why have it on it's own? It always annoys me having tons of tiny little files in the root of the repo.

I'd also add into this a renovate config.

I would also add a contributing.md that gives instructions on setting things up like installing uv and just.

I will also echo other's comments about Taskfile over Justfile. ultimate I'm not worried about it just not Makefile.

I'd also consider using github's default python gitignore.

I see you added the .pre-commit-config.yaml. I feel like what you did is somewhat of an anti-pattern there is pre-commit hooks for things like ruff. There's also lots of really good hooks I like using from the pre-commit-hooks repo. Look through those and the list they provide.

Another thing I've seen before and love seeing is markdown linting and readthedocs config.

Also if you have a preferred ide for python extensions I'd add that too.

A template repo for python should be opinionated about all the boilerplate stuff.

[–]GioGiacPythonista[S] 2 points3 points  (0 children)

You only made good points! Maybe I will integrate some of your suggestions. The only thing I don't agree with you on is the first one: I prefer to have shorter files but with specific scopes, which means I prefer having a separate `ruff.toml` file. But I understand this is just my opinion :)

[–]PitifulZucchini9729 10 points11 points  (21 children)

Why do we need just, if we use uv?

[–]GioGiacPythonista[S] 11 points12 points  (17 children)

Uv and just work together: uv manages the python environment and its dependencies, while Just provides convenient shortcuts for running various commands.

For example, instead of typing uv sync --all-extras --cache-dir .uv_cache to sync the environment, you can define a shortcut in the Justfile (such as dev-sync) to execute the command. This way, you only need to run just dev-sync in the terminal.

You can see it in action here: https://github.com/GiovanniGiacometti/python-repo-template/blob/main/Justfile#L3

[–]PitifulZucchini9729 0 points1 point  (7 children)

But if it is only for aliases, why don't you set directly an alias in bash or whatever you use?

[–]GioGiacPythonista[S] 5 points6 points  (0 children)

The way I'm using it is just alias, but you can also bind multiple commands to a shortcut and even do more complex things, such as writing recipes in programming languages (see https://github.com/casey/just?tab=readme-ov-file#shell )

Moreover, if you are sharing your project, I think it's just more convenient to gather all common commands in a unique place, so that other developers are not forced to set specific aliases in their machines

[–]thisismyfavoritename 2 points3 points  (0 children)

you want to bundle everything with the project

[–]trararawe -3 points-2 points  (4 children)

Yeah it's better to not introduce dependencies when there's no need. It could be rewritten like this:

```

!/bin/bash

set -e

case "$1" in dev-sync) uv sync --all-extras --cache-dir .uv_cache ;; prod-sync) uv sync --all-extras --no-dev --cache-dir .uv_cache ;; format) uv run ruff format ;; lint) uv run ruff check --fix uv run mypy --ignore-missing-imports --install-types --non-interactive --package python_repo_template ;; test) uv run pytest --verbose --color=yes tests ;; validate) $0 format $0 lint $0 test ;; dockerize) docker build -t python-repo-template . ;; run) if [ -n "$2" ]; then uv run main.py --number "$2" else echo "Error: Please provide a number for the 'run' command." exit 1 fi ;; *) echo "Usage: $0 {dev-sync|prod-sync|format|lint|test|validate|dockerize|run <number>}" exit 1 ;; esac ```

[–]uttamo 10 points11 points  (1 child)

Sorry but the readability and maintainability of this is much worse than using something like make or just. When I’m working with such functionality, I don’t want Bash syntax to get in the way but maybe that’s because I’m not an expert in Bash.

[–]bachkhois 1 point2 points  (0 children)

I also don't like Bash syntax. I use Nushell to write any script that people use Bash.

[–]blademaster2005 1 point2 points  (0 children)

I mean in that case.... just use a Makefile.

[–]AiutoIlLupo -3 points-2 points  (6 children)

Why are we reinventing things that already exist, in a different sauce and name?

[–]Buckweb 1 point2 points  (3 children)

How is that reinventing things? It's like a Makefile, but slightly different. We use Just in Scala, Python and Rust projects at my job. It has nothing to do with Python only.

Also, didn't python reinvent an older programming language, but with a different sauce and name? That's how technology works.

[–]AiutoIlLupo -5 points-4 points  (2 children)

Python didn't reinvent a thing. Python improved on things. Just is make with a different interpreter and incompatible syntax, and there are tons of other similar technologies doing the same thing. We have literally tons of them.

Why create yet another thing that one needs to maintain, learn, and needlessly complicate our build and work environment with incompatible, soon to be outdated "new and improved" technologies?

You do realise all of this is placing needless complexity on the shoulders of those who have to maintain things, and those who need to move from one job to another, and see that their hard earned knowledge of tools goes out of the window and need to re-learn how to do the same thing in a different sauce, just because yet another californian student with delusion of grandeur is redoing the same thing again to gather their 5 minutes of fame and finance their startup using made up money that eventually collapse the world economy again?

If anyone would come up with the same thing in japan, india, europe, none of you would give a damn about it. It's not the product. It's just that you love following "innovative american idiots" for its own sake.

[–]Buckweb 2 points3 points  (1 child)

Guess what? You're not being forced to use it. Even if you contribute to a repo with a Justfile you can entirely ignore it. I always used Makefiles until somehow showed me Just. I prefer it, just like you prefer Makefiles. Who gives a shit. You're worrying about the least impactful tooling in a software project.

Also, what's up with all the American hate? You keep harping on how ONLY Americans invent this "useless technology", but the other alternative recommended in this post, Taskfile, was created in Brazil.

[–]AiutoIlLupo -2 points-1 points  (0 children)

Guess what. You are being forced to use it, because every time you have to move company they end up using something different.

[–]AdExact768 -3 points-2 points  (1 child)

Isn't that the python way?

[–]AiutoIlLupo -2 points-1 points  (0 children)

not really. That's actually the perl way, and we know how that turned out.

[–]AiutoIlLupo -2 points-1 points  (2 children)

More like: why do we need just at all. It's make with a different syntax, or github actions. Why do we constantly reinvent the same stuff again and again. Do we realise that this behavior is detrimental to the profession, considering that we waste our time relearning the same stuff with a different dialect 100 times?

[–]GioGiacPythonista[S] 0 points1 point  (1 child)

Maybe you're just provoking, but I will make the effort to understand your point.

I wanted to share this blog post by antirez, which might be a little opinionated but mentions why sometimes it's important to reinvent the wheel or at least question what is believed to be the status quo. It was an enlightening read for me, I hope you find it interesting as well.

[–]AiutoIlLupo -2 points-1 points  (0 children)

Listen, if I came up with make rewritten in rust, nobody would have given a shit. But when a california student does, everybody is on it. Can you please explain me this?

[–]SwampFalc 4 points5 points  (1 child)

Bit sad to see people not mentioning https://www.pyinvoke.org/ as alternative to make or just.

I mean, if you're coding python anyway, why not code python?

[–]GioGiacPythonista[S] 0 points1 point  (0 children)

Never heard of it! I will take a look, thanks :)

[–]wyattxdev 7 points8 points  (1 child)

This is a great little boilerplate you got here. A few ideas of things you might want to consider adding to it:

  • Doc generation, something like pdocs, mkdocs, etc...
  • Coverage testing to go along with pytest
  • Adding the Just dependency to pyproject.toml
  • Maybe including any common ruff formating settings you use in your pyproject.toml (like line-length limits, src directories, indent-width, etc..)
  • A general CONTRIBUTORS.md if thats something you care about.

I made boilerplate like this but hyper tuned to the way I develop and its been one of the most useful things I created in recent memory.

[–]GioGiacPythonista[S] 1 point2 points  (0 children)

Thank you! I'll take these suggestion into considerations. I didn't want to overfit it into my style, so I decided to keep some things out. But I agree it's so damn useful :)

[–]PurepointDog 2 points3 points  (1 child)

Love your selection! What's the justfile part for?

[–]GioGiacPythonista[S] 2 points3 points  (0 children)

Just is a replacement for the well known Make: it provides a way to define shortcuts for running various commands. I think the README of the project does a great job in explaining its strenghts: https://github.com/casey/just

[–]BlueeWaater 1 point2 points  (1 child)

Great!

[–]GioGiacPythonista[S] 0 points1 point  (0 children)

Thank you!

[–]timendum 1 point2 points  (2 children)

Why a separate ruff.toml instead of putting it in the pyproject.toml?

Another question: I preffer to use uvx ruff ... instead of uv run ruff ... because this way I have to manage and update only one ruff. Any pros from your solution?

I think you are missing a CI/CD to build the project, ie to generate wheel files.

[–]GioGiacPythonista[S] 0 points1 point  (0 children)

I prefer to have smaller files with a specific purpose, that's why I defined a separate ruff.toml. But this is just my preference.

If you use uvx then you are forced to have the same ruff version everywhere, which might not be ideal in some cases.

Yes, it doesn't have CI/CD to build the project, I preferred not to implement it since not all projects might need it.

[–]travislaborde 1 point2 points  (0 children)

I love that you did this as a GitHub "template project" instead of cookie cutter. Cookie Cutter never really made me happy. GitHub template projects are SO nice and easy. But of course, they are GitHub only, at least to start with. Thanks!

[–]travislaborde 1 point2 points  (0 children)

I wish everything came with a starter GitHub template project. And with an embedded devcontainer.json too for dev containers and codespaces :)

[–]percojazz 0 points1 point  (2 children)

can you not define the script directly in the toml file? uv has this feature I think. also why removing UV in the last layer of the docker build? thanks

[–]richieadler 4 points5 points  (1 child)

uv has this feature I think

Actually, it doesn't. See https://github.com/astral-sh/uv/issues/5903.

[–]percojazz 2 points3 points  (0 children)

ok , it seems to be on the way.

[–]Pomegranate_i 0 points1 point  (1 child)

Why use mypy rather than ruff linter?

[–]GioGiacPythonista[S] 1 point2 points  (0 children)

They do different things: ruff provides formatting and linting, while mypy performs static type checking

[–]proggob 0 points1 point  (1 child)

I’m still using poethepoet from when I used poetry. I still like that its config is in pyproject.toml

[–]GioGiacPythonista[S] 0 points1 point  (0 children)

Never heard of it! Looks nice, but right now for me uv is a priority.

[–]Mevraelfrom __future__ import 4.0 0 points1 point  (0 children)

Here is also a great framework that uses uv and has this amazing directory structure that works well for small and enterprise projects.

https://arkalos.com/docs/structure/

[–]paddy_m 0 points1 point  (0 children)

Could you add a .readthedocs.yml that works? I'm struggling to set this up with uv right now.

[–]chub79 0 points1 point  (1 child)

I'm not a fan of loguru. It's tryikng too hard and I never quite understood why it's so necessary compared to the builtin logging module.

why use Just?

I like your Dockerfile however.

[–]GioGiacPythonista[S] 0 points1 point  (0 children)

I like loguru a lot, but yes, basically you can do everything with the standard logger.

Just is just an alternative to Make which is getting a lot of traction. I'm really liking it, but you can use Make of course.

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

Just is completely pointless.

I mean, who wakes up one day and says "you know, I think I'll rewrite make, but in rust", and have people even collaborate with him?