Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native python types #8559

Closed
wants to merge 15 commits into from
Closed

Native python types #8559

wants to merge 15 commits into from

Conversation

wouterdb
Copy link
Contributor

@wouterdb wouterdb commented Jan 6, 2025

Description

Basic native python types

For the documentation, I would add a separate ticket

Self Check:

Strike through any lines that are not applicable (~~line~~) then check the box

  • Attached issue to pull request
  • Changelog entry
  • Type annotations are present
  • Code is clear and sufficiently documented
  • No (preventable) type errors (check using make mypy or make mypy-diff)
  • Sufficient test cases (reproduces the bug/tests the requested feature)
  • Correct, in line with design
  • End user documentation is included or an issue is created for end-user documentation (add ref to issue here: )
  • If this PR fixes a race condition in the test suite, also push the fix to the relevant stable branche(s) (see test-fixes for more info)

@wouterdb wouterdb requested review from bartv and sanderr January 6, 2025 13:16
src/inmanta/plugins.py Outdated Show resolved Hide resolved
@wouterdb wouterdb marked this pull request as ready for review January 6, 2025 13:40
python_to_model = {
str: inmanta_type.String(),
float: inmanta_type.Float(),
numbers.Number: inmanta_type.Number(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to support this, given that the number type has been deprecated?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deprecated, but not gone.

I have no strong opinion on this

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After giving it some more thought, me neither. At some level I'd prefer not to integrate deprecated types into new features, but if it's as simple as this I guess it doesn't hurt too much either. The only thing is perhaps that we have the opportunity to make a clean cut here, to be more visible than the current deprecation warning.

@@ -267,11 +345,13 @@ def resolve_type(self, plugin: "Plugin", resolver: Namespace) -> inmanta_type.Ty
return self._resolved_type

if not isinstance(self.type_expression, str):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so for iso 8.1 we also aim to evaluate strings here, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had understood that every string is considered to be an inmanta type

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would prefer to also support python type strings, because that's what you would expect when writing Python. But that doesn't have to be now necessarily. What I had in mind was to

  1. try to evaluate the annotation as a python type expression (converting any strings, even nested ones, to types)
  2. convert python type expression to dsl type expression

If 1 fails -> fall back to dsl type parsing.

But I don't feel too strongly about this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want that. That would create the same type of problem that yaml has (is 00:00:00:00 a number?), that parsing outcome depends on context you (often) can't know. if a type 'string' is somehow in scope, the typing changes

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps a key element in my reasoning is that I approach it from the idea that the pure dsl annotations would eventually disappear, leaving only the typing.Annotated when you really need them. With that reasoning, the Python parsing would always be the primary, and the DSL parsing would become only a backwards compatibility fallback.

But if that's not how you see it evolve, then perhaps I agree with you, given your argument above. I still think it can have unintuitive aspects of its own, but the typing.Annotated should give us a way to work around any we encounter to give ourselves time to discover where we really want to go.

Comment on lines 229 to 235
str: inmanta_type.String(),
float: inmanta_type.Float(),
numbers.Number: inmanta_type.Number(),
int: inmanta_type.Integer(),
bool: inmanta_type.Bool(),
dict: inmanta_type.LiteralDict(),
list: inmanta_type.LiteralList(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm actually not sure about hardcoding this mapping to the type objects. I think I'd prefer to use the existing inmanta.ast.type.TYPES mapping, so that if we ever change something there it is reflected in both flows. Then this mapping here would simply be a mapping to the model types as represented in the model, e.g. str -> "string".

Copy link
Contributor Author

@wouterdb wouterdb Jan 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upon closer consideration, what I had here was even wrong.

inmanta.ast.type.TYPES is the internal attribute types, which are subtly different from what is here, mapping it twice won't be correct / consistent with what existed already.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not following with this. From your comment I understood that you agreed we should use inmanta.ast.type.TYPES, but from the implementation I think I misunderstood? Why should we not use it?

Copy link
Contributor Author

@wouterdb wouterdb Jan 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should use inmanta.ast.type.TYPES because:

  1. I consider the type object the interface, so we should decouple on that
  2. inmanta.ast.type.TYPES maps attribute type string to inmanta types, but this is subtly different from the type mapping at the plugin boundary. E.g. "dict": LiteralDict(), Vs dict: inmanta_type.TypedDict(inmanta_type.Type()), And this is a pre-existing condition

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, clear!

src/inmanta/plugins.py Outdated Show resolved Hide resolved
if len(other_types) == 1:
return inmanta_type.NullableType(to_dsl_type(other_types[0]))
# TODO: optional unions
return inmanta_type.AnyType()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we create a follow-up ticket to fix all these any types by 8.1? They could cause some very unintuitive behavior I think.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then again, aren't these relatively easy to handle?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will ticketize and we'll see when we get them.

(e.g. the unions could be easy, but then again, we don't use unions anywhere yet, so they could also be very difficult)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you mark the tickets for the 8.1 release? I don't want to keep all these Any's for longer than required, so I'd like to address it (on iso8) while we can still consider it a non-breaking change (because it's in there now but not documented).

src/inmanta/plugins.py Outdated Show resolved Hide resolved

if typing_inspect.is_generic_type(python_type):
origin = typing.get_origin(python_type)
if origin is Sequence:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the design document: "Collection and Sequence as return types will require minor changes to DynamicProxy.unwrap, which now contains isinstance(item, list)" The same might go for Mapping, I haven't checked.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is still missing.

src/inmanta/plugins.py Outdated Show resolved Hide resolved
src/inmanta/plugins.py Show resolved Hide resolved
src/inmanta/plugins.py Show resolved Hide resolved
src/inmanta/plugins.py Show resolved Hide resolved
src/inmanta/plugins.py Outdated Show resolved Hide resolved
@wouterdb wouterdb requested a review from sanderr January 6, 2025 14:53
src/inmanta/plugins.py Outdated Show resolved Hide resolved
@wouterdb wouterdb requested a review from sanderr January 7, 2025 09:00
src/inmanta/ast/type.py Outdated Show resolved Hide resolved
src/inmanta/plugins.py Show resolved Hide resolved
Comment on lines 237 to 238
Mapping: inmanta_type.TypedDict(inmanta_type.Type()),
collections.abc.Mapping: inmanta_type.TypedDict(inmanta_type.Type()),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something weird is going on here. Mapping should be from collections.abc. I think it's good to support both, but then the qualification must be the other way around (or both qualified if you prefer). Because it's very likely that the import will be updated at some point.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for sequence.

if not args:
return inmanta_type.List()
return inmanta_type.TypedList(to_dsl_type(args[0]))
if origin in [collections.abc.Sequence, list, typing.Sequence]:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not Collection?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Collection also contains Mapping and Set, so I think that is a bit too wide?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But we're not accepting subclasses anymore, only those exact annotations. A list is a collection, so that would be acceptable for input args. And we can easily construct lists from it so it's acceptable as return type.

But I'm not too attached to it, it's not that common.

return inmanta_type.TypedDict(to_dsl_type(args[1]))
return inmanta_type.TypedDict(to_dsl_type(args[1]))
else:
raise TypingException(None, f"invalid type {python_type}, dictionary types should be Mapping or dict")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. I hadn't considered this, but it's good to have a clear message!

One suggestion

Suggested change
raise TypingException(None, f"invalid type {python_type}, dictionary types should be Mapping or dict")
raise TypingException(None, f"invalid type {python_type}, dictionary types should be Mapping (preferred) or dict")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the order implies preference?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess. I mostly meant to say that I think we should discourage the dict, even if we accept it, because it's simply not accurate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could even not mention the dict, but it is the most familiar to most people

@sanderr
Copy link
Contributor

sanderr commented Jan 7, 2025

@wouterdb FYI, there are still some threads that I consider relevant. You may have just skipped over them for now, but if you missed them this should bring them to your attention. I resolved all threads that I consider no longer relevant. Some of the others are only really relevant for follow-up tickets (e.g. whether or not we want to support stringified Python type expressions), others are decisions that need to be made (e.g. typing.Annotated now or not), and some are things that I'm just confused about now.

@wouterdb wouterdb requested a review from sanderr January 7, 2025 14:39
Copy link
Contributor

@sanderr sanderr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we're starting to align on a middle ground. One final note on something that I'd prefer to see differently, but I can let it slide if you disagree.

if python_type in python_to_model:
return python_to_model[python_type]

return inmanta_type.Type()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of this fallback to Any. This may easily give the user the false impression that they're getting type validation, while that is not actually the case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my proposed extensions on slack

@wouterdb wouterdb added the merge-tool-ready This ticket is ready to be merged in label Jan 7, 2025
@inmantaci
Copy link
Contributor

Processing this pull request

@inmantaci inmantaci removed the merge-tool-ready This ticket is ready to be merged in label Jan 7, 2025
@inmantaci
Copy link
Contributor

Failed to merge changes into iso7 due to merge conflict. Please open a pull request for these branches separately by cherry-picking the commit that was made on the branch master (git cherry-pick 881d1d5).

@inmantaci
Copy link
Contributor

Merged into branches master in 881d1d5

inmantaci pushed a commit that referenced this pull request Jan 7, 2025
# Description

Basic native python types

For the documentation, I would add a separate ticket

# Self Check:

Strike through any lines that are not applicable (`~~line~~`) then check the box

- [x] Attached issue to pull request
- [x] Changelog entry
- [x] Type annotations are present
- [x] Code is clear and sufficiently documented
- [x] No (preventable) type errors (check using make mypy or make mypy-diff)
- [x] Sufficient test cases (reproduces the bug/tests the requested feature)
- [x] Correct, in line with design
- [ ] End user documentation is included or an issue is created for end-user documentation (add ref to issue here: )
- [ ] If this PR fixes a race condition in the test suite, also push the fix to the relevant stable branche(s) (see [test-fixes](https://internal.inmanta.com/development/core/tasks/build-master.html#test-fixes) for more info)
@inmantaci
Copy link
Contributor

Not closing this pull request due to previously commented issues for some of the destination branches. Please open a separate pull request for those branches by cherry-picking the relevant commit. You can safely close this pull request and delete the source branch.

@inmantaci
Copy link
Contributor

This branch was not deleted as it seems to still be in use.

wouterdb added a commit that referenced this pull request Jan 7, 2025
Basic native python types

For the documentation, I would add a separate ticket

Strike through any lines that are not applicable (`~~line~~`) then check the box

- [x] Attached issue to pull request
- [x] Changelog entry
- [x] Type annotations are present
- [x] Code is clear and sufficiently documented
- [x] No (preventable) type errors (check using make mypy or make mypy-diff)
- [x] Sufficient test cases (reproduces the bug/tests the requested feature)
- [x] Correct, in line with design
- [ ] End user documentation is included or an issue is created for end-user documentation (add ref to issue here: )
- [ ] If this PR fixes a race condition in the test suite, also push the fix to the relevant stable branche(s) (see [test-fixes](https://internal.inmanta.com/development/core/tasks/build-master.html#test-fixes) for more info)
@wouterdb wouterdb mentioned this pull request Jan 7, 2025
@wouterdb wouterdb closed this Jan 7, 2025
@wouterdb wouterdb deleted the issue/native_types branch January 8, 2025 08:28
inmantaci pushed a commit that referenced this pull request Jan 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants