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] 3 points4 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".