Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat: addasdict() andto_json() methods to Gitlab Objects#1872

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
nejch merged 2 commits intomainfromjlvillal/as_dict
Jul 20, 2022
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletionsdocs/api-usage.rst
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -214,6 +214,38 @@ the value on the object is accepted:
issue.my_super_awesome_feature_flag = "random_value"
issue.save()

You can get a dictionary representation copy of the Gitlab Object. Modifications made to
the dictionary will have no impact on the GitLab Object.

* `asdict()` method. Returns a dictionary representation of the Gitlab object.
* `attributes` property. Returns a dictionary representation of the Gitlab
object. Also returns any relevant parent object attributes.

.. note::

`attributes` returns the parent object attributes that are defined in
`object._from_parent_attrs`. What this can mean is that for example a `ProjectIssue`
object will have a `project_id` key in the dictionary returned from `attributes` but
`asdict()` will not.


.. code-block:: python

project = gl.projects.get(1)
project_dict = project.asdict()

# Or a dictionary representation also containing some of the parent attributes
issue = project.issues.get(1)
attribute_dict = issue.attributes

You can get a JSON string represenation of the Gitlab Object. For example:

.. code-block:: python

project = gl.projects.get(1)
print(project.to_json())
# Use arguments supported by `json.dump()`
print(project.to_json(sort_keys=True, indent=4))

Base types
==========
Expand Down
32 changes: 19 additions & 13 deletionsgitlab/base.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,7 +15,9 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import copy
import importlib
import json
import pprint
import textwrap
from types import ModuleType
Expand DownExpand Up@@ -142,15 +144,26 @@ def __getattr__(self, name: str) -> Any:
def __setattr__(self, name: str, value: Any) -> None:
self.__dict__["_updated_attrs"][name] = value

def asdict(self, *, with_parent_attrs: bool = False) -> Dict[str, Any]:
data = {}
if with_parent_attrs:
data.update(copy.deepcopy(self._parent_attrs))
data.update(copy.deepcopy(self._attrs))
data.update(copy.deepcopy(self._updated_attrs))
return data

@property
def attributes(self) -> Dict[str, Any]:
return self.asdict(with_parent_attrs=True)

def to_json(self, *, with_parent_attrs: bool = False, **kwargs: Any) -> str:
return json.dumps(self.asdict(with_parent_attrs=with_parent_attrs), **kwargs)

def __str__(self) -> str:
data = self._attrs.copy()
data.update(self._updated_attrs)
return f"{type(self)} => {data}"
return f"{type(self)} => {self.asdict()}"

def pformat(self) -> str:
data = self._attrs.copy()
data.update(self._updated_attrs)
return f"{type(self)} => \n{pprint.pformat(data)}"
return f"{type(self)} => \n{pprint.pformat(self.asdict())}"

def pprint(self) -> None:
print(self.pformat())
Expand DownExpand Up@@ -241,13 +254,6 @@ def encoded_id(self) -> Optional[Union[int, str]]:
obj_id = gitlab.utils.EncodedId(obj_id)
return obj_id

@property
def attributes(self) -> Dict[str, Any]:
d = self.__dict__["_updated_attrs"].copy()
d.update(self.__dict__["_attrs"])
d.update(self.__dict__["_parent_attrs"])
return d


class RESTObjectList:
"""Generator object representing a list of RESTObject's.
Expand Down
97 changes: 97 additions & 0 deletionstests/unit/test_base.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -36,6 +36,16 @@ class FakeManager(base.RESTManager):
_path = "/tests"


class FakeParent:
id = 42


class FakeManagerWithParent(base.RESTManager):
_path = "/tests/{test_id}/cases"
_obj_cls = FakeObject
_from_parent_attrs = {"test_id": "id"}


@pytest.fixture
def fake_gitlab():
return FakeGitlab()
Expand All@@ -46,6 +56,21 @@ def fake_manager(fake_gitlab):
return FakeManager(fake_gitlab)


@pytest.fixture
def fake_manager_with_parent(fake_gitlab):
return FakeManagerWithParent(fake_gitlab, parent=FakeParent)


@pytest.fixture
def fake_object(fake_manager):
return FakeObject(fake_manager, {"attr1": "foo", "alist": [1, 2, 3]})


@pytest.fixture
def fake_object_with_parent(fake_manager_with_parent):
return FakeObject(fake_manager_with_parent, {"attr1": "foo", "alist": [1, 2, 3]})


class TestRESTManager:
def test_computed_path_simple(self):
class MGR(base.RESTManager):
Expand DownExpand Up@@ -306,3 +331,75 @@ def test_repr(self, fake_manager):

FakeObject._id_attr = None
assert repr(obj) == "<FakeObject>"

def test_attributes_get(self, fake_object):
assert fake_object.attr1 == "foo"
result = fake_object.attributes
assert result == {"attr1": "foo", "alist": [1, 2, 3]}

def test_attributes_shows_updates(self, fake_object):
# Updated attribute value is reflected in `attributes`
fake_object.attr1 = "hello"
assert fake_object.attributes == {"attr1": "hello", "alist": [1, 2, 3]}
assert fake_object.attr1 == "hello"
# New attribute is in `attributes`
fake_object.new_attrib = "spam"
assert fake_object.attributes == {
"attr1": "hello",
"new_attrib": "spam",
"alist": [1, 2, 3],
}

def test_attributes_is_copy(self, fake_object):
# Modifying the dictionary does not cause modifications to the object
result = fake_object.attributes
result["alist"].append(10)
assert result == {"attr1": "foo", "alist": [1, 2, 3, 10]}
assert fake_object.attributes == {"attr1": "foo", "alist": [1, 2, 3]}

def test_attributes_has_parent_attrs(self, fake_object_with_parent):
assert fake_object_with_parent.attr1 == "foo"
result = fake_object_with_parent.attributes
assert result == {"attr1": "foo", "alist": [1, 2, 3], "test_id": "42"}

def test_asdict(self, fake_object):
assert fake_object.attr1 == "foo"
result = fake_object.asdict()
assert result == {"attr1": "foo", "alist": [1, 2, 3]}

def test_asdict_no_parent_attrs(self, fake_object_with_parent):
assert fake_object_with_parent.attr1 == "foo"
result = fake_object_with_parent.asdict()
assert result == {"attr1": "foo", "alist": [1, 2, 3]}
assert "test_id" not in fake_object_with_parent.asdict()
assert "test_id" not in fake_object_with_parent.asdict(with_parent_attrs=False)
assert "test_id" in fake_object_with_parent.asdict(with_parent_attrs=True)

def test_asdict_modify_dict_does_not_change_object(self, fake_object):
result = fake_object.asdict()
# Demonstrate modifying the dictionary does not modify the object
result["attr1"] = "testing"
result["alist"].append(4)
assert result == {"attr1": "testing", "alist": [1, 2, 3, 4]}
assert fake_object.attr1 == "foo"
assert fake_object.alist == [1, 2, 3]

def test_asdict_modify_dict_does_not_change_object2(self, fake_object):
# Modify attribute and then ensure modifying a list in the returned dict won't
# modify the list in the object.
fake_object.attr1 = [9, 7, 8]
assert fake_object.asdict() == {
"attr1": [9, 7, 8],
"alist": [1, 2, 3],
}
result = fake_object.asdict()
result["attr1"].append(1)
assert fake_object.asdict() == {
"attr1": [9, 7, 8],
"alist": [1, 2, 3],
}

def test_asdict_modify_object(self, fake_object):
# asdict() returns the updated value
fake_object.attr1 = "spam"
assert fake_object.asdict() == {"attr1": "spam", "alist": [1, 2, 3]}

[8]ページ先頭

©2009-2025 Movatter.jp