Skip to main content
  1. Posts/

Giving Python another chance

·4 mins
Backend Python Ruff Poetry
Clément Sauvage
Clément Sauvage
I like to make softwares

The python case #

I love python. I (unfortunately) started programming with it. Python rigged my understanding of programming concepts at the beginning of my journey. It seemed like the norm.

I still use python a lot, there’s so much use cases it is great at:

  • Scripting
  • Developing apps
  • Data analysis and visualization
  • Machine learning
  • Automation

Every developer can find a situation in which python is the best tool for the job.

But nothing is perfect. We all know the weaknesses of the snake are also pretty annoying to deal with sometimes.

I am not going to complain about aspects that seem vain to me, like the syntax being different from most others programming languages.

No, to me, a better python would differ on these three main aspects:

  • Static typing
  • A universal linter and formatter
  • A better package management
  • Better performances

Static typing and better linting #

This one is pretty obvious. The dynamic nature of python makes it versatile, which is one of the reason it is fast to code with.

But it also makes it less reliable and provoke a lot of bugs that are usually found at runtime. Even when developing, types help you understand what you are working with.

A solution that seems to be notorious for all the python developers, is to use MyPy to perform Static Typing checks. I really like it, and I had a great experience working professionally with it recently. But, I cannot understand if I am doing something wrong, but why do I find myself in cases where it seems more headaches that help ?

You end up having to provide stubs for every single library you use in your project. Some libraries have unfortunately incomplete support for MyPy. I find myself writing ultra extensive types when calling code from other libraries. For instance, look at this mess when calling the OpenAI library:

async def complete(self, prompt: str) -> str:
        response: typing.Dict[
            str, typing.List[typing.Dict[str, str]]
        ] = await openai.Completion.create(
            engine=self.model, prompt=prompt, **OPENAI_COMPLETION_OPTIONS
        return response["choices"][0]["text"]

A universal linter and formatter #

What linter am I supposed to use with python ? There are multiple options: Flake8, Pylint, Pylama… Why do choose which linter to use should be even a topic we are debating on ? It’s a bit of an unfortunate situation, IMHO. But on the bright side, there seems to be one that most people agree to accept recently: ruff

This new one is blazingly fast (thanks to the crab-lang) and seems to handle most cases.

I unfortunately still find myself to face issues I cannot seem to find a solution for. When external libraries use optional types, I cannot enforce the object type after its creation.

src/ error: Item "None" of "Optional[Message]" has no attribute "text"  [union-attr]
src/ error: Incompatible types in assignment (expression has type "Union[str, None, Any]", variable has type "str")  [assignment]
src/ error: Item "None" of "Optional[Message]" has no attribute "chat"  [union-attr]
src/ error: Item "None" of "Optional[Message]" has no attribute "reply_text"  [union-attr]

A better package management #

Since Python was the first programming language I worked with, I didn’t understand why people were criticizing its package manager.

I lacked perspective, and didn’t realize it was a bit heavy to download all the dependencies for each of my project in an isolated folder which acted as a virtual environment. It was impossible to keep track of the dependencies otherwise, since the standard way to lock the list and versions of the dependencies is to ask to write the content of your virtual-environment to an external `txt` file: `pip freeze > requirements.txt`.

We can still write the dependencies by hand, remove the versions to always get the latest version. But you will probably end up with incompatibility issues, impossibility to find the dependency for a given python version (since it is not shared in the `requirements.txt`).

It’s a bit of a mess. But I understand how it can be a faster way to get started for smaller projects, which is where Python excels at.

Poetry seems to be the solution to python’s dependencies issue. Keeping track of the dependencies in different environments, versions, not tying the downloaded libraries to one specific project, etc… I still have to take a look at it and experience it, but it definitely sounds like going for Poetry makes all the other Python packages manager looks obsolete.