python: Implement version-agnostic Protocol types - #114
Conversation
Signed-off-by: Arthit Suriyawongkul <arthit@gmail.com>
Signed-off-by: Arthit Suriyawongkul <arthit@gmail.com>
Signed-off-by: Arthit Suriyawongkul <arthit@gmail.com>
Coverage reportClick to see where and how coverage changed
This report was generated by python-coverage-comment-action |
||||||||||||||||||||||||
Signed-off-by: Arthit Suriyawongkul <arthit@gmail.com>
Signed-off-by: Arthit Suriyawongkul <arthit@gmail.com>
Signed-off-by: Arthit Suriyawongkul <arthit@gmail.com>
Signed-off-by: Arthit Suriyawongkul <arthit@gmail.com>
Signed-off-by: Arthit Suriyawongkul <arthit@gmail.com>
Follows --include-main pattern Signed-off-by: Arthit Suriyawongkul <arthit@gmail.com>
Signed-off-by: Arthit Suriyawongkul <arthit@gmail.com>
Signed-off-by: Arthit Suriyawongkul <arthit@gmail.com>
Signed-off-by: Arthit Suriyawongkul <arthit@gmail.com>
|
This PR may need update after #124 got merged, as the protocols may need to have the new |
|
#124 merged. Need to review this PR (may need few revision). Put to draft. |
Follow the updated test.ttl Signed-off-by: Arthit Suriyawongkul <arthit@gmail.com>
For cheap check of is_release, without the need to actually load the model Signed-off-by: Arthit Suriyawongkul <arthit@gmail.com>
Signed-off-by: Arthit Suriyawongkul <arthit@gmail.com>
Signed-off-by: Arthit Suriyawongkul <arthit@gmail.com>
Signed-off-by: Arthit Suriyawongkul <arthit@gmail.com>
|
|
||
| def __getattr__(name: str) -> Any: | ||
| # PEP 562 lazy access: each branch imports only what it needs. | ||
| if name == "__all__": |
There was a problem hiding this comment.
Why doesn't this need 'main' from cmd (and protocols)?
Also, is it possible to maybe use model.__all__ instead of manually filtering?
There was a problem hiding this comment.
For first question, it is intentional since cmd and protocols will not be usually used (as a library) and we like to avoid loading anything that is not likely to be used.
For cmd and main, same rational as removing main from spdx-python-model binding generation at spdx/spdx-python-model#51
For protocols, its main use case is for type check and it is better to keep it separate because if we allow them in mix in top-level import * all *Protocol classes will be loaded during runtime (no exactly harmful, but unnecessary waste memory - every model class will have its own *Protocol class counterpart).
Users who want them still able to access them by using fully qualified name.
--
For 2nd one, we can use model.__all__ too but since currently model doesn't have __all__, we have to define it there.
Do it in model.py.j2 is more straightforward, we explicitly say what we like to export. It is also cheaper (do it a generation time). The downside is maintenance, you have to maintain the full list of __all__.
Do it in __init__.py.j2 retrospectively is more expensive but lower maintenance, you inspect actual available symbols, then filter it. You maintain only a small blacklist - which in this case tend to be stable. Since the imports tend to be only few times per process, I chose to do it in init.
Each approach has its own risks though. The static explicit list in model can under-export. The dynamic filter in init can over-export.
Signed-off-by: Arthit Suriyawongkul <arthit@gmail.com>
Signed-off-by: Arthit Suriyawongkul <arthit@gmail.com>
Signed-off-by: Arthit Suriyawongkul <arthit@gmail.com>
Signed-off-by: Arthit Suriyawongkul <arthit@gmail.com>
Signed-off-by: Arthit Suriyawongkul <arthit@gmail.com>
This PR adds
src/shacl2code/lang/templates/python/protocols.py.j2template to generates Protocol structure types for classes in the generated bindings.Given two versions of a model (v1 and v2; v2 is backward compatible with v1) to shacl2code to generate Python bindings (one at a time), it will generate two bindings that look similar but v1.SomeClass and v2.SomeClass are considered distinct. They are not sharing a common parent type.
This makes it difficult for a downstream user who wants to write a function like:
Because if it defines
SomeClassasv1.SomeClass, it will be incompatible withv2.SomeClass(and vice versa).This PR provides the generation of
protocols.SomeClasswhich is a structural type that accepts any class with the same shape (has similar members), sov1.SomeClassandv2.SomeClasscan be both accepted, even they are unrelated.This function will work across all versions of the model (provided that they are keeping backward compatibility to previous versions -- not removing any members in newer versions):