-
Notifications
You must be signed in to change notification settings - Fork 12
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
reduce the boilerplate #61
Comments
@JoshCu I was actually thinking I would remove the abstract base class altogether and switch to protocols as @BSchilperoort suggested in #58. That, I think, would solve your first issue. It wouldn't address your second issue, though. For that maybe we could have a library of base classes one could inherit from for building new BMI components that use standard implementations? We've kind of done things like this for our data components that, for example, wrap xarray.Datasets or a geotiffs. |
@JoshCu This is a little off-topic, but we've started a new forum, https://forum.csdms.io, and it would great if we could get more discussions going on BMI. Please check it out and sign up if you're interested. |
I haven't worked with protocols before but that looks like a great idea! I could see subclasses like scalarbmi and gridbmi being useful for those types of models. For the second issue I can't think of a robust way to deal with it. Those variables could be added to the subclasses along with default getter methods, but I'm not aware of a clean way to make sure that e.g. def get_start_time( self ):
if not hasattr(self, '_start_time'):
raise AttributeError('_start_time not found, either implement method get_start_time or use _start_time to store model start time')
return self._start_time
I'll take a look :) Would it be better to move discussion to there or keep it here? I'm more used to github but if the forum has more visibilty I'm happy to use that. I've only interacted with python-bmi but I could open it up to a more general question about multiple default interfaces in other languages if they might also benefit? |
I really don't have a good sense for this yet. I like your suggestion of using GitHub for more specific issues/questions and the forum for more general. |
Having done the same thing, @JoshCu, I certainly can sympathize haha. If you want to avoid the boilerplate, why not use some python meta-programming? from __future__ import annotations
import abc
def raise_not_implemented(cls: type[abc.ABC]):
def not_implemented(name: str):
def inner(self, *args, **kwargs):
raise NotImplementedError(name)
return inner
t = type(
cls.__qualname__,
(cls,),
{name: not_implemented(name) for name in cls.__abstractmethods__},
)
def wrapper(*args, **kwargs):
return t(*args, **kwargs)
return wrapper
class Base(abc.ABC):
@abc.abstractmethod
def foo(self) -> str: ...
@abc.abstractmethod
def bar(self) -> str: ...
@raise_not_implemented
class Subclass(Base):
def foo(self) -> str:
return "horray"
s = Subclass()
s.bar() # NotImplementedError: bar This isn't perfect, but could be improved upon to avoid undesirable boilerplate with a small amount of code and effort. @JoshCu, thoughts? |
Truthfully, I've not worked much with python like that and I wasn't entirely sure how to do it, but that looks pretty neat! I'll try it out and report back if I find an alternative worth suggesting. |
I have been (slowly) working on a python BMI sdk package, and part of that implements an intermediate https://github.com/hellkite500/bmi_pytorch/blob/main/bmi_sdk/bmi_sdk/bmi_minimal.py |
@JoshCu, that is totally fair! We are getting into magical spell territory. This might be a little more approachable?
|
Perhaps more of a discussion than an issue; can some of the methods just raise not implemented by default and drop the abstractmethod decorator that forces subclasses to explicitly implement every function?
For example if the gridded methods didn't require explicit implementation, I wouldn't need all of this.
This might be too far from the standard, could some of the common values also have default implementations? There are probably other examples too but I've left these unchanged from the bmi python example I copied. Could they also be defaults in the base class?
The text was updated successfully, but these errors were encountered: