Skip to content

Commit

Permalink
Merge branch 'main' into changelog-changes
Browse files Browse the repository at this point in the history
  • Loading branch information
tonyandrewmeyer authored Mar 22, 2024
2 parents 54073b1 + 8097cca commit 240e90b
Show file tree
Hide file tree
Showing 13 changed files with 694 additions and 91 deletions.
5 changes: 5 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# 2.12.0

* Updated Pebble Notices `get_notices` parameter name to `users=all` (previously `select=all`).
* Added `Model.get_cloud_spec` which uses the `credential-get` hook tool to get details of the cloud where the model is deployed.

# 2.11.0

Features:
Expand Down
4 changes: 4 additions & 0 deletions ops/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@
'BindingMapping',
'BlockedStatus',
'CheckInfoMapping',
'CloudCredential',
'CloudSpec',
'ConfigData',
'Container',
'ContainerMapping',
Expand Down Expand Up @@ -270,6 +272,8 @@
BindingMapping,
BlockedStatus,
CheckInfoMapping,
CloudCredential,
CloudSpec,
ConfigData,
Container,
ContainerMapping,
Expand Down
21 changes: 12 additions & 9 deletions ops/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -507,9 +507,10 @@ def restore(self, snapshot: Dict[str, Any]):
class RelationCreatedEvent(RelationEvent):
"""Event triggered when a new relation is created.
This is triggered when a new relation to another app is added in Juju. This
This is triggered when a new integration with another app is added in Juju. This
can occur before units for those applications have started. All existing
relations should be established before start.
relations will trigger `RelationCreatedEvent` before :class:`StartEvent` is
emitted.
"""
unit: None # pyright: ignore[reportIncompatibleVariableOverride]
"""Always ``None``."""
Expand All @@ -521,7 +522,7 @@ class RelationJoinedEvent(RelationEvent):
This event is triggered whenever a new unit of a related
application joins the relation. The event fires only when that
remote unit is first observed by the unit. Callback methods bound
to this event may set any local unit settings that can be
to this event may set any local unit data that can be
determined using no more than the name of the joining unit and the
remote ``private-address`` setting, which is always available when
the relation is created and is by convention not deleted.
Expand All @@ -539,13 +540,13 @@ class RelationChangedEvent(RelationEvent):
the callback method bound to this event.
This event always fires once, after :class:`RelationJoinedEvent`, and
will subsequently fire whenever that remote unit changes its settings for
will subsequently fire whenever that remote unit changes its data for
the relation. Callback methods bound to this event should be the only ones
that rely on remote relation settings. They should not error if the settings
are incomplete, since it can be guaranteed that when the remote unit or
application changes its settings, the event will fire again.
that rely on remote relation data. They should not error if the data
is incomplete, since it can be guaranteed that when the remote unit or
application changes its data, the event will fire again.
The settings that may be queried, or set, are determined by the relation's
The data that may be queried, or set, are determined by the relation's
interface.
"""

Expand Down Expand Up @@ -1539,7 +1540,9 @@ def __init__(self, name: str, raw: '_StorageMetaDict'):
self.multiple_range = None
if 'multiple' in raw:
range = raw['multiple']['range']
if '-' not in range:
if range[-1] == '+':
self.multiple_range = (int(range[:-1]), None)
elif '-' not in range:
self.multiple_range = (int(range), int(range))
else:
range = range.split('-')
Expand Down
42 changes: 19 additions & 23 deletions ops/framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -779,18 +779,11 @@ class SomeObject:
RuntimeError: if bound_event or observer are the wrong type.
"""
if not isinstance(bound_event, BoundEvent):
raise RuntimeError(
raise TypeError(
f'Framework.observe requires a BoundEvent as second parameter, got {bound_event}')
if not isinstance(observer, types.MethodType):
# help users of older versions of the framework
if isinstance(observer, charm.CharmBase):
raise TypeError(
'observer methods must now be explicitly provided;'
' please replace observe(self.on.{0}, self)'
' with e.g. observe(self.on.{0}, self._on_{0})'.format(
bound_event.event_kind))
raise RuntimeError(
f'Framework.observe requires a method as third parameter, got {observer}')
raise TypeError(
f"Framework.observe requires a method as the 'observer' parameter, got {observer}")

event_type = bound_event.event_type
event_kind = bound_event.event_kind
Expand All @@ -801,27 +794,23 @@ class SomeObject:
if hasattr(emitter, "handle"):
emitter_path = emitter.handle.path
else:
raise RuntimeError(
raise TypeError(
f'event emitter {type(emitter).__name__} must have a "handle" attribute')

# Validate that the method has an acceptable call signature.
sig = inspect.signature(observer)
# Self isn't included in the params list, so the first arg will be the event.
extra_params = list(sig.parameters.values())[1:]

method_name = observer.__name__

assert isinstance(observer.__self__, Object), "can't register observers " \
"that aren't `Object`s"
observer_obj = observer.__self__
if not sig.parameters:
raise TypeError(
f'{type(observer_obj).__name__}.{method_name} must accept event parameter')
elif any(param.default is inspect.Parameter.empty for param in extra_params):
# Allow for additional optional params, since there's no reason to exclude them, but
# required params will break.

# Validate that the method has an acceptable call signature.
sig = inspect.signature(observer, follow_wrapped=False)
try:
sig.bind(EventBase(None)) # type: ignore
except TypeError as e:
raise TypeError(
f'{type(observer_obj).__name__}.{method_name} has extra required parameter')
f'{type(observer_obj).__name__}.{method_name} must be callable with '
"only 'self' and the 'event'") from e

# TODO Prevent the exact same parameters from being registered more than once.

Expand Down Expand Up @@ -951,6 +940,13 @@ def _reemit(self, single_event_path: Optional[str] = None):
# Regular call to the registered method.
custom_handler(event)

else:
logger.warning(
f"Reference to ops.Object at path {observer_path} has been garbage collected "
"between when the charm was initialised and when the event was emitted. "
"Make sure sure you store a reference to the observer."
)

if event.deferred:
deferred = True
else:
Expand Down
Loading

0 comments on commit 240e90b

Please sign in to comment.