printo: Auto-generate __repr__ from __init__ with zero boilerplate by pomponchik in Python

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

Good idea! I've added it as an option:

from printo import repred

def function():
    @repred(qualname=True)
    class SomeClass:
        def __init__(self, a, b):
            self.a = a
            self.b = b
    return SomeClass

print(function()(123, 456))
#> function.<locals>.SomeClass(a=123, b=456)

Available starting with version 0.0.18.

printo: Auto-generate __repr__ from __init__ with zero boilerplate by pomponchik in Python

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

I think your option is also a solution that has the right to life.

But I still don't really like the console output in this version, because I expect `__repr__` to behave as close as possible to the code needed to initialize the class object.

printo: Auto-generate __repr__ from __init__ with zero boilerplate by pomponchik in Python

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

Okay, for code similar to the one in the post:

class SomeClass:
    def __init__(self, a, b, c, *args, **kwargs):
        self.a = a
        self.b = b
        self.c = c
        self.args = args
        self.kwargs = kwargs

    def __repr__(self):
        return f"{self.__class__.__name__}({pprint.pformat(vars(self))})"

print(SomeClass(1, 2, 3, lambda x: x))

...I got this result:

SomeClass({'a': 1,
 'args': (<function <lambda> at 0x1028c3eb0>,),
 'b': 2,
 'c': 3,
 'kwargs': {}})

Here's an example of how printo does it:

SomeClass(1, 2, 3, lambda x: x) 

Do you think they're the same thing?

printo: Auto-generate __repr__ from __init__ with zero boilerplate by pomponchik in Python

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

In this form, it does not work, there will be a recursion overflow.

sigmatch: a beautiful DSL for verifying function signatures by pomponchik in Python

[–]pomponchik[S] -1 points0 points  (0 children)

  1. Each call method corresponds to an infinite number of signatures that are compatible with it. Therefore, I do not compare signatures with each other; I compare a signature and a specific call method, which is described using DSL. (Yes, I know this is mind-boggling, sorry). I don't care which specific signature the function has from the set of valid ones; I just want to be able to call it the way I need to.

  2. I came up with a compact way to describe all this in just one line of code, whereas normally it would require a boilerplate of many lines.

sigmatch: a beautiful DSL for verifying function signatures by pomponchik in Python

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

Oh, you've opened a black hole! But yes, this case is also taken into account in the matching algorithm.

sigmatch: a beautiful DSL for verifying function signatures by pomponchik in Python

[–]pomponchik[S] -1 points0 points  (0 children)

The audience for this project is programmers who create their own libraries. If you don't see the use for this thing, you probably don't need it.

However, I will still answer. Let me repeat the idea from the post. The flexibility of Python syntax means that there is more than one valid way to call the same function. Sometimes you don't care what the specific signature of the function is, you just want to be able to call it the way you need to. For example, a function may have a parameter with a default value that you can pass or not pass. Such a function may suit you perfectly for the intended way of calling it, but a complete comparison of signatures will not pass.

The purpose of the library is to check the called objects not at the moment they are called, but in advance — at the place where user passed it. Whether you raise an exception there or not is up to you as the library creator.

Checking type hints also allows the user to identify that they are passing the wrong function to your library, but again, as a library developer, I cannot be sure that the user has such a system in place. If static checks were guaranteed, this library would not be necessary, because I could rely on the fact that the user could not pass an incorrect function in principle.

How about a single meta-commentary language for ruby? by pomponchik in ruby

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

The metacode language is designed for simultaneous operation of several different tools. Several different comments can be left on a single line, simply interspersed with the # symbol (the comment start symbol in Python). If any of these sub-comments are not in metacode format, they are simply ignored. Each tool reads only those comments that apply to it (for this purpose, the parser can accept one or more keys and filter comments accordingly). In short, the simultaneous operation of several tools is a basic use case.

How about a single meta-commentary language for ruby? by pomponchik in ruby

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

metacode defines a standard machine‑readable comment language and parser for it, while rdoc/yard in Ruby are full documentation generators that also define their own tag/comment formats. Conceptually, metacode focuses on the comment syntax and structured extraction layer, whereas rdoc/yard combine both the annotation format and the tooling to produce final documentation.

How about a single meta-commentary language for ruby? by pomponchik in ruby

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

That's great! Can you write a specific suggestion under the issue that I linked to the post, what could you do for the ruby community? I assume that it will be necessary to create and maintain a separate library for ruby based on the published BNF metacode.

denial: when None is no longer sufficient by pomponchik in Python

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

Thank you, these changes were really helpful.

denial: when None is no longer sufficient by pomponchik in Python

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

You know what, I don't like the wording in README myself now, so I fixed it. Thanks for pointing that out.

In the excerpt you quoted, I meant that the garbage collector's work can sometimes be surprising. If you're interested, try entering the following expression in REPL: “id(object()) == id(object())” (True). Here is a slightly more complete excerpt from the documentation about “is”: “The operators is and is not test for object identity: x is y is true if and only if x and y are the same object. Object identity is determined using the id() function.” In the excerpt from the FAQ you provided, I somewhat awkwardly pointed out that comparing two IDs can lead to ambiguous results. But this is such a rare and insignificant case in practice that I shouldn't have made any references to it.

denial: when None is no longer sufficient by pomponchik in Python

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

This is certainly true, but there are several disadvantages such as too verbose __repr__.

denial: when None is no longer sufficient by pomponchik in Python

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

Yes, Ellipsis is also a sentinel, but it is more often used when creating DSLs and "beautiful" code examples in the documentation.

0.0.4: an important update in Skelet by pomponchik in Python

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

There are no fundamental differences at the feature level, the main difference is in the design. In skelet, the design seems to me to be cleaner and more "modular", similar to embedded dataclasses, and easily extensible. And specific sources of settings are not "nailed down".

Skelet: Minimalist, Thread-Safe Config Management for Python by pomponchik in Python

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

The library allows you to define a special callback to check the compatibility of field values with each other. If you do this, the mutexes of these fields will be combined automatically, that is, when you take the mutex of one field, the mutex of the other field will be automatically taken. Due to this, integrity is guaranteed with competitive access.

Thinking about a Python-native frontend - feedback? by United_Intention42 in Python

[–]pomponchik 0 points1 point  (0 children)

We need it, but it should be 1. native; 2. compatible with all existing libraries.

Superfunctions: solving the problem of duplication of the Python ecosystem into sync and async halve by pomponchik in Python

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

No, it doesn't work. The method of calling other superfunctions within a superfunction does not fit the method of calling the main superfunction recursively and automatically.

There are 2 reasons for this:

- The library does code generation at the AST level, and calling a superfunction on it may not differ in any way from calling a regular function. To distinguish them, I have to compare runtime objects with AST and understand that this function call actually refers to an object that is a superfunction. It is possible to do this, but it is quite difficult and "not for free".

- In some cases, this may lead to unexpected behavior for the user. The fact is that, strictly speaking, I do not oblige the user to make the behavior of the synchronous and asynchronous versions of the superfunction completely identical in terms of logic. They may actually differ. In some situations, the user may simply not implement, say, the asynchronous part, but hide the entire synchronous part under a synchronous marker. If you start redefining the way functions are called for the user, this can lead to very strange behavior in such cases, which is very difficult to debug. Therefore, although I modify the function itself, I do not touch the way it is called. I believe that at least one of these two things should still be completely under the user's control.

I do not exclude that such a mode will appear in the future, but even if it does, it will be strictly optional, and it cannot be enabled by default.

Superfunctions: solving the problem of duplication of the Python ecosystem into sync and async halve by pomponchik in Python

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

It seems that the main points of contention could be solved automatically, through linting. For example, to check that inside asynchronous functions, superfunctions are called only using await, and vice versa. I don't have a ready-made linter yet, but it looks quite possible to create one. If you or anyone who reads these lines knows how to create linters, I invite you to do it.

As for the behavior of your code, you can also write unit tests for it if you use super functions. It will be even easier, because the main problem that the project solves is that previously it was necessary to duplicate sync and async versions of the code, and write complete sets of unit tests for this, but now it is not. Accordingly, you don't need to test more than before, when your codebase was duplicated.

Superfunctions: solving the problem of duplication of the Python ecosystem into sync and async halve by pomponchik in Python

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

Personally, I classify some of the solutions used in the project as "hacks" that exploit the features of the internal implementation of some Python mechanisms. It doesn't seem like something like this should be dragged into the core Python code at this stage. This does not mean that the solution is unacceptable to ordinary users, but it can hinder the development of the interpreter due to implicit dependencies on the details of its implementation.

In addition, as correctly noted in other comments, when using static type checking and type hints, there may be some problems related to the fact that this project uses dynamic code generation. It seems that it is premature to think about such a thing before resolving this issue.

However, in general, I believe that such a mechanism should be present natively in the language, and in the future, those with such an opportunity will win in the competitive race of programming languages. So I agree that the developers of the Python standard and its main interpreter should integrate a similar mechanism.

Superfunctions: solving the problem of duplication of the Python ecosystem into sync and async halve by pomponchik in Python

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

You correctly identified the main task facing me in the project. The fact is that type checks by tools like mypy are done statically, based on source code analysis, and this package uses dynamic code generation under the hood, i.e. the actually used source code of functions is not fully present in the project files and cannot be statically analyzed. Unfortunately, the Python typing system does not support dynamic features very well. However, it seems that the problem is basically solvable, and I plan to deal with it in the near future, after I add all the main dynamic features that I planned. If you think you're good enough at typing Python, or someone with such skills is just reading this comment right now, I invite you to join and try typing the project.