Code generation for ThingClient subclasses#89
Draft
rwb27 wants to merge 6 commits into
Draft
Conversation
|
I remember us talking about this. Now that I am somewhat finished with my obligations, I will have deeper look at the latest changes in labthings. FYI, I already released pydantic based property types from this code base as part of my repository. |
Collaborator
Author
|
I'm going to bump this to 0.0.13 as it's not yet critical. I am keen to get in in though, as it will really improve the experience of using lab things from e.g. a python notebook. |
This generates Python code for a module containing a client. The Python code generated for the test thing executes successfully, though the name of the class is not quite right yet.
Dataschemas of type Object that have defined properties are now converted to Pydantic models. This should allow much better autocompletion. The inconsistency of using `dict[str, any]` as a fallback rankles, but I think it's the best option.
2c68771 to
bff6809
Compare
This should be significantly more secure: I believe it should now be impossible for a malcious Thing Description to create dangerous Python code. In doing this, I've simplified the way the client creates properties - I need to check if it type hints them correctly. There are still a few things to fix: * Default values probably want to be `...` rather than constants, so they're filled in server-side. * This doesn't yet validate/convert return values. * This isn't tested enough and probably doesn't actually work! * We need configurability: as a minimum, recursion limits and fallback to `Any` so it fails gracefully if given a bad TD.
bff6809 to
4a46348
Compare
This commit adds: * a limit on recursion * a bunch of tidying up * many more comments * cleaner generated code * generated docstrings for actions, properties, and Things. * thing client properties now derive from BaseDescriptor - this required a few changes, but makes the generated code neater. * BaseDescriptor may now be defined on non-Thing classes.
This moves the single file out of the folder, keeping the module name the same. I've also provided the `type_params` argument on Python > 3.11.
Barecheck - Code coverage reportTotal: 96.39%Your code coverage diff: -0.58% ▾ Uncovered files and lines |
This compares unparsed strings rather than AST nodes, as the latter don't properly show as equal. `ast.compare` will help here, but not until we drop a few Python versions.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Currently,
ThingClientsubclasses are generated on-the-fly from a Thing Description. This "gets the job done" and means it's always possible to have a client that's up to date with the server. However, it would be nice to support static analysis, e.g. type checking, autocompletion, etc. with importable client classes for specific instruments.Functionality
This PR implements client code generation, specifically:
ThingClientand named as per thetitleof the Thing Description.get_property,set_propertyas provided byThingClientDataSchemaobjects don't distinguish whether extra properties are allowed or not.invoke_action....is used to distinguish between explicitly setNonevalues, and unspecified values (i.e. not included in the JSON). This allows the server to fill in default values, which is safer than trying to generate default values in the Python code.astis used to generate a syntax tree that's then turned into code. This has the significant advantage that:Schema conversion
Conversion from
DataSchemato Python types is implemented partially. Types are converted recursively, with support for:integer->intnumber->floatboolean->boolstring->stranyOf->Unionnull->Nonearray->list(if an item type is specified, it's converted recursively) ortuple(if several item types are specified)object->dict[str, Any]or a generatedpydantic.BaseModelsubclass (ifpropertiesare specified)Anyobjectschemas will be recursively converted topydanticmodels. This can be turned off by disabling recursion, and more fine-grained control may be helpful.Still to do
Before this can be merged, we need to add a few things:
ThingClient.from_urlnow uses this module.Blobin the output (rather than autogenerating a model for it).