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

What is optimal solution for automatically registering objects with their parents? #156

Open
spencerahill opened this issue Mar 19, 2017 · 4 comments

Comments

@spencerahill
Copy link
Owner

spencerahill commented Mar 19, 2017

C.f. discussion in #155. Still not seeing this 100% clearly, so it's a bit stream-of-consciousness.

A lot of our problems revolve around: how to "register" objects. Right now, this is largely by hand: passing a regions kwarg to Proj, or defining projects and variables objects at the module level of your object library. From what I can gather, the most pythonic method for registering classes is using metaclasses (e.g. here).

However, we are trying to register particular class instances, not the classes themselves. So do we just do it at the __init__ level rather than the __new__ level that the metaclass uses? Edit: __new__ does get called each time a class instance is created, before __init__. So perhaps the metaclass route remains. But the issue below of how to automatically choose the parent object also remains.

Supposing the answer is 'yes', how do we specify the default "parent" object to which this object should be registered, without the user having to explicitly specify it every time? Also, is this even feasible given the current (counter-intuitive) call signatures that require that children objects must be defined prior to their parents (e.g. Run before Model)?

(If that was fixed and we didn't care about requiring that user input, this would be easy: have a kwarg in each class's __init__ that specifies its parent object. E.g. example_model = Model(..., proj=example_proj, ...))

One idea: create an ObjectLibrary class, something like

class ObjectLibrary(object):
    def __init__(self):
        self.projects = {}
        self.variables = {} 

So e.g. near the top of our example object library file example_obj_lib.py would be

object_library = ObjectLibrary(
    # note sure what exactly would go here
)

Then what if we just imposed the convention that any Proj object automatically finds the object named object_library at the same scope and has that as its parent. Although is that technically feasible? How would one find a particular object in a namespace based on its variable name?

This also makes me realize that, if multiple objects of the same parent type (e.g. Model) are defined within the same scope (e.g. the same module), there's no choice but to specify which one is the parent in any child objects (e.g. Runs) that are defined in that scope.

@spencerahill
Copy link
Owner Author

(If that was fixed and we didn't care about requiring that user input, this would be easy: have a kwarg in each class's init that specifies its parent object. E.g. example_model = Model(..., proj=example_proj, ...))

This was incomplete. We would also need to implement the logic such that, in this example, example_model would be added to example_proj.models.

@spencerahill
Copy link
Owner Author

Although is that technically feasible? How would one find a particular object in a namespace based on its variable name?

Potential solution: use the builtin dir() function. e.g. something like [obj for obj in dir() if obj == 'object_library']

@spencerahill
Copy link
Owner Author

Another idea: functions that set current default parent, potentially implemented as context managers.

I.e. something like

model1 = Model(...)
model2 = Model(...)

aospy.config.set_default_parent(model=model1)
run1 = Run(...)

...

aospy.config.set_default_parent(model=model2)
run2 = Run(...)

And the result would be run1 and any others defined before the next aospy.config.set_default_parent call would have model1 as their parent, and run2 and subsequent would have model2 as their parent, etc.

This could maybe work nicely as context managers. I.e. something like

model1 = Model(...)
model2 = Model(...)

with model1 as aospy.config.default_parent_model:
    run1 = Run(...)
    ...

with model2 as aospy.config.default_parent_model:
    run2 = Run(...)

@spencerahill
Copy link
Owner Author

Some more half-baked thoughts re: the context managers idea

with aospy.config.set_default_parent(model=model1):
    run1 = Run(...)
  • They can be defined for classes by defining __enter__ and __exit__ methods, or they can be defined using the @contextlib.contextmanager decorator applied to a generator function that calls yield exactly once. The latter option would look something like
@contextlib.contextmanager
def set_default_parent(model=None, ...):
    if model is not None:
        orig = aospy.config.default_parent
        aospy.config.default_parent['model'] = model
    yield
    aospy.config.default_parent['model'] = orig

assuming we implemented aospy.config.default_parent as a dict with keys for each object level. There would be logic in Run something like

class Runl(object):
     def __init__(self, ...):
         ...
         if model is None:
             self.model = aospy.config.default_parent['model']

But we this starts to hit against our vision of relaxing the proj/model/run hierarchy, c.f. #111 . Would also benefit from having a core aospy object, c.f. #34...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant