-
Notifications
You must be signed in to change notification settings - Fork 187
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
♻️ REFACTOR: NodeRepositoryMixin
-> NodeRepository
#5439
Conversation
@override | ||
def put_object_from_filelike(self, handle: io.BufferedReader, path: str): | ||
"""Store the byte contents of a file in the repository. | ||
|
||
:param handle: filelike object with the byte content to be stored. | ||
:param path: the relative path where to store the object in the repository. | ||
:raises TypeError: if the path is not a string and relative path. | ||
:raises aiida.common.exceptions.ModificationNotAllowed: when the node is sealed and therefore immutable. | ||
""" | ||
self.check_mutability() | ||
|
||
if isinstance(handle, io.StringIO): | ||
handle = io.BytesIO(handle.read().encode('utf-8')) | ||
|
||
if isinstance(handle, tempfile._TemporaryFileWrapper): # pylint: disable=protected-access | ||
if 'b' in handle.file.mode: | ||
handle = io.BytesIO(handle.read()) | ||
else: | ||
handle = io.BytesIO(handle.read().encode('utf-8')) | ||
|
||
self._repository.put_object_from_filelike(handle, path) | ||
self._update_repository_metadata() | ||
|
||
@override | ||
def put_object_from_file(self, filepath: str, path: str): | ||
"""Store a new object under `path` with contents of the file located at `filepath` on the local file system. | ||
|
||
:param filepath: absolute path of file whose contents to copy to the repository | ||
:param path: the relative path where to store the object in the repository. | ||
:raises TypeError: if the path is not a string and relative path, or the handle is not a byte stream. | ||
:raises aiida.common.exceptions.ModificationNotAllowed: when the node is sealed and therefore immutable. | ||
""" | ||
self.check_mutability() | ||
self._repository.put_object_from_file(filepath, path) | ||
self._update_repository_metadata() | ||
|
||
@override | ||
def put_object_from_tree(self, filepath: str, path: str = None): | ||
"""Store the entire contents of `filepath` on the local file system in the repository with under given `path`. | ||
|
||
:param filepath: absolute path of the directory whose contents to copy to the repository. | ||
:param path: the relative path where to store the objects in the repository. | ||
:raises TypeError: if the path is not a string and relative path. | ||
:raises aiida.common.exceptions.ModificationNotAllowed: when the node is sealed and therefore immutable. | ||
""" | ||
self.check_mutability() | ||
self._repository.put_object_from_tree(filepath, path) | ||
self._update_repository_metadata() | ||
|
||
@override | ||
def delete_object(self, path: str): | ||
"""Delete the object from the repository. | ||
|
||
:param key: fully qualified identifier for the object within the repository. | ||
:raises TypeError: if the path is not a string and relative path. | ||
:raises FileNotFoundError: if the file does not exist. | ||
:raises IsADirectoryError: if the object is a directory and not a file. | ||
:raises OSError: if the file could not be deleted. | ||
:raises aiida.common.exceptions.ModificationNotAllowed: when the node is sealed and therefore immutable. | ||
""" | ||
self.check_mutability() | ||
self._repository.delete_object(path) | ||
self._update_repository_metadata() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless I am missing something, none of these methods, actually changed anything from the ones they are overriding!?
Perhaps the check_mutability
was meant to also check for whether the node is sealed, but that is certainly not the case at present?
Note, I also need to update the documentation, but will wait until the changes are approved in principle to do that |
a6a257f
to
fc666e3
Compare
def warn_deprecation(message: str, version: int, stacklevel=2) -> None: | ||
"""Warns about a deprecation for a future aiida-core version. | ||
|
||
Warnings are activated if the `AIIDA_WARN_v{major}` environment variable is set to `True`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does it work like this though? Why do we need an env variable to tell the code which deprecation warnings to print instead of just printing all of them?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the plugin developers and users can choose when they want to switch them on, usually when they get round to refactoring their code.
This is exactly how other libraries do their deprecations: https://docs.sqlalchemy.org/en/14/changelog/migration_20.html#migration-to-2-0-step-two-turn-on-removedin20warnings
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You "turn-on" the deprecations when running your test suite, then apply all the fixes, until there are no more warnings, job done.
This is exactly how I updated aiida-core to the sqlalchemy v2 API, and how I updated all the code in these PRs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically, we don't want to be flooding users with loads of deprecation warnings for v3, as soon as they update to v2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mmm, I see. I agree that it is good to give them the option to silence them, but should they not be "on" by default? I would bet a lot of people may have AiiDA scripts that need update and are not necessarily being tested in this CI automated way. If we don't notify them by default they run the risk of only noticing this once the deprecated features are dropped and then they get errors instead of warnings.
On the other hand, why do we need to distinguish between versions for this (AIIDA_WARN_v{major}
)? Why not have just AIIDA_DEPRECATION_WARNINGS
? At any given time there will only be one set of deprecations, why do we need the selectivity?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because they are only just upgrading to v2, and likely won't want to be hit with lots of deprecation warnings also for v3. These can be turned on by default, in a later v2 release, closer to a v3 release.
I am generally in favor of the idea of this PR and those that follow up in the same vein, however, there are a few questions and for simplicity will collect them here.
I think we should discuss these points, and any other that may be relevant, with the team and then come to a decision. Then we can move on with the implementation. |
Exactly this was the point; it's going to be discouraging for users, updating from v1, if the have to work out too many changes at the same time.
Two things to note here:
|
Note, |
On the other hand, these changes would be relatively easy for
Yes, kind of, but the JSONable data could also be written to the file repository and then it is still not queryable. So if you maintain that distinction, it is not automatically clear what can be queried for through the
I am not sure about this. Sure maybe for custom |
Well you won't after #5088 |
Well not so much. aiida-upgrade (and basically any refactor tool) uses static analysis to find what to change, it can't understand dynamic variables. whatever = load_node(1)
x = whatever.attributes["a"] it would need to change this, currently, to: whatever = load_node(1)
x = whatever.attrs.all["a"] But it has no idea that
That's good though 😄 |
fc666e3
to
f6e8c0a
Compare
f6e8c0a
to
b9efc45
Compare
This is a pre-cursor to moving methods off of the `Node`
b9efc45
to
84058dd
Compare
superseded by #5472 |
This PR forms part of addressing #4976 (comment) and related to https://en.wikipedia.org/wiki/Composition_over_inheritance,
and reduces the number of attributes/methods on
aiida.orm.Node
from 83 to 68.Essentially, it deprecates the following methods (whilst still allowing them to be used):
and instead places them under the
Node.ctx.repository.
"namespace", e.g.Node.ctx.repository.copy_tree
.Not only does this reduce the number of methods, it also makes it a lot clearer to users, the purpose of these methods.
All deprecated methods, can still be used as before, via "re-routing" in
Node.__getattr__
.So as not to suddenly hit users with many deprecation warnings,
the
aiida.common.warnings.warn_deprecation
function has been implemented,which only "activates" warnings when the environmental variable
AIIDA_WARN_v3
is set totrue
or 1.This implementation was inspired by https://docs.sqlalchemy.org/en/14/changelog/migration_20.html#migration-to-2-0-step-two-turn-on-removedin20warnings
Deprecation warnings show as e.g.