From 6d3969cefd88ece66ba57ba4682cc85fb38a7dea Mon Sep 17 00:00:00 2001 From: Aleksander Wojnarowicz Date: Mon, 16 Jan 2023 15:02:24 +0100 Subject: [PATCH 1/4] add get_root_object method and handling of run-level methods invocation --- src/neptune/new/handler.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/neptune/new/handler.py b/src/neptune/new/handler.py index 85816f5cd..584ce2e99 100644 --- a/src/neptune/new/handler.py +++ b/src/neptune/new/handler.py @@ -62,6 +62,12 @@ parse_path, ) from neptune.new.internal.value_to_attribute_visitor import ValueToAttributeVisitor +from neptune.new.metadata_containers import ( + Model, + ModelVersion, + Project, + Run, +) from neptune.new.types.atoms.file import File as FileVal from neptune.new.types.type_casting import cast_value_for_extend from neptune.new.types.value_copy import ValueCopy @@ -86,6 +92,7 @@ def inner_fun(self: "Handler", *args, **kwargs): ExtendDictT = Union[Collection[Any], Dict[str, "ExtendDictT"]] +NeptuneObject = Union[Run, Model, ModelVersion, Project] class Handler: @@ -94,7 +101,7 @@ class Handler: SYSTEM_STAGE_ATTRIBUTE_PATH: NeptuneCannotChangeStageManually, } - def __init__(self, container: "MetadataContainer", path: str): + def __init__(self, container: NeptuneObject, path: str): super().__init__() self._container = container self._path = path @@ -113,6 +120,14 @@ def __getitem__(self, path: str) -> "Handler": def __setitem__(self, key: str, value) -> None: self[key].assign(value) + def __getattr__(self, item: str): + run_level_methods = {"exists", "get_structure", "get_run_url", "print_structure", "stop", "sync", "wait"} + + if item in run_level_methods: + raise AttributeError("Run-level method used on Handler object.") + + return object.__getattribute__(self, item) + def _get_attribute(self): """Returns Attribute defined in `self._path` or throws MissingFieldException""" attr = self._container.get_attribute(self._path) @@ -125,6 +140,9 @@ def container(self) -> "MetadataContainer": """Returns the container that the attribute is attached to""" return self._container + def get_root_object(self) -> NeptuneObject: + return self._container + @check_protected_paths def assign(self, value, wait: bool = False) -> None: """Assigns the provided value to the field. From 437857fc3cc5350748655737c1a7e24be6db348b Mon Sep 17 00:00:00 2001 From: Aleksander Wojnarowicz Date: Mon, 16 Jan 2023 15:03:13 +0100 Subject: [PATCH 2/4] add @check_protected_paths deco to append and extend methods --- src/neptune/new/handler.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/neptune/new/handler.py b/src/neptune/new/handler.py index 584ce2e99..1189cd53b 100644 --- a/src/neptune/new/handler.py +++ b/src/neptune/new/handler.py @@ -318,6 +318,7 @@ def log( self._container.set_attribute(self._path, attr) attr.log(value, step=step, timestamp=timestamp, wait=wait, **kwargs) + @check_protected_paths def append( self, value: Union[dict, Any], @@ -365,6 +366,7 @@ def append( value = ExtendUtils.validate_and_transform_to_extend_format(value) self.extend(value, step, timestamp, wait, **kwargs) + @check_protected_paths def extend( self, values: ExtendDictT, From f12aac099686d2780adffc1e717eedceed57f3a5 Mon Sep 17 00:00:00 2001 From: Aleksander Wojnarowicz Date: Mon, 16 Jan 2023 15:13:10 +0100 Subject: [PATCH 3/4] fix type-related error --- src/neptune/new/handler.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/neptune/new/handler.py b/src/neptune/new/handler.py index 1189cd53b..2911fc866 100644 --- a/src/neptune/new/handler.py +++ b/src/neptune/new/handler.py @@ -24,6 +24,7 @@ Iterable, Iterator, List, + NewType, Optional, Union, ) @@ -62,12 +63,6 @@ parse_path, ) from neptune.new.internal.value_to_attribute_visitor import ValueToAttributeVisitor -from neptune.new.metadata_containers import ( - Model, - ModelVersion, - Project, - Run, -) from neptune.new.types.atoms.file import File as FileVal from neptune.new.types.type_casting import cast_value_for_extend from neptune.new.types.value_copy import ValueCopy @@ -75,6 +70,8 @@ if TYPE_CHECKING: from neptune.new.metadata_containers import MetadataContainer + NeptuneObject = NewType("NeptuneObject", MetadataContainer) + def validate_path_not_protected(target_path: str, handler: "Handler"): path_protection_exception = handler._PROTECTED_PATHS.get(target_path) @@ -92,7 +89,6 @@ def inner_fun(self: "Handler", *args, **kwargs): ExtendDictT = Union[Collection[Any], Dict[str, "ExtendDictT"]] -NeptuneObject = Union[Run, Model, ModelVersion, Project] class Handler: @@ -101,7 +97,7 @@ class Handler: SYSTEM_STAGE_ATTRIBUTE_PATH: NeptuneCannotChangeStageManually, } - def __init__(self, container: NeptuneObject, path: str): + def __init__(self, container: "NeptuneObject", path: str): super().__init__() self._container = container self._path = path @@ -140,7 +136,7 @@ def container(self) -> "MetadataContainer": """Returns the container that the attribute is attached to""" return self._container - def get_root_object(self) -> NeptuneObject: + def get_root_object(self) -> "NeptuneObject": return self._container @check_protected_paths From 650437d358fe3ab12eeead53ff8a8dfd5ba476f4 Mon Sep 17 00:00:00 2001 From: Aleksander Wojnarowicz Date: Mon, 16 Jan 2023 16:27:29 +0100 Subject: [PATCH 4/4] improve error message --- src/neptune/new/handler.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/neptune/new/handler.py b/src/neptune/new/handler.py index 2911fc866..d32b79351 100644 --- a/src/neptune/new/handler.py +++ b/src/neptune/new/handler.py @@ -120,7 +120,17 @@ def __getattr__(self, item: str): run_level_methods = {"exists", "get_structure", "get_run_url", "print_structure", "stop", "sync", "wait"} if item in run_level_methods: - raise AttributeError("Run-level method used on Handler object.") + raise AttributeError( + "You're invoking an object-level method on a handler for a namespace" "inside the object.", + f""" + For example: You're trying run[{self._path}].{item}() + but you probably want run.{item}(). + + To obtain the root object of the namespace handler, you can do: + root_run = run[{self._path}].get_root_object() + root_run.{item}() + """, + ) return object.__getattribute__(self, item)