biocutils 0.3.1.dev0__tar.gz → 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/CHANGELOG.md +9 -1
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/PKG-INFO +1 -1
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/setup.py +1 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/__init__.py +2 -1
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/factor.py +56 -2
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/float_list.py +1 -1
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/integer_list.py +1 -1
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/named_list.py +106 -2
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/names.py +49 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/show_as_cell.py +1 -1
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/which.py +1 -1
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils.egg-info/PKG-INFO +1 -1
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_BooleanList.py +2 -2
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_Factor.py +55 -4
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_FloatList.py +1 -1
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_IntegerList.py +1 -1
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_NamedList.py +75 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_Names.py +62 -1
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_StringList.py +2 -2
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_biocobject.py +13 -13
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_combine_sequences.py +1 -1
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_factorize.py +1 -1
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/.coveragerc +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/.github/workflows/publish-pypi.yml +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/.github/workflows/run-tests.yml +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/.gitignore +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/.pre-commit-config.yaml +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/.readthedocs.yml +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/AUTHORS.md +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/CONTRIBUTING.md +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/LICENSE.txt +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/README.md +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/docs/Makefile +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/docs/_static/.gitignore +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/docs/authors.md +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/docs/changelog.md +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/docs/conf.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/docs/contributing.md +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/docs/index.md +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/docs/license.md +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/docs/readme.md +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/docs/requirements.txt +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/pyproject.toml +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/setup.cfg +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/_utils_combine.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/assign.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/assign_rows.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/assign_sequence.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/bioc_object.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/boolean_list.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/combine.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/combine_columns.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/combine_rows.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/combine_sequences.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/convert_to_dense.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/extract_column_names.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/extract_row_names.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/factorize.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/get_height.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/intersect.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/is_high_dimensional.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/is_list_of_type.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/is_missing_scalar.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/map_to_index.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/match.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/normalize_subscript.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/package_utils.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/print_truncated.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/print_wrapped_table.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/relaxed_combine_columns.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/relaxed_combine_rows.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/reverse_index.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/string_list.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/subset.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/subset_rows.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/subset_sequence.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/table.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils/union.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils.egg-info/SOURCES.txt +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils.egg-info/dependency_links.txt +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils.egg-info/not-zip-safe +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils.egg-info/requires.txt +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/src/biocutils.egg-info/top_level.txt +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/conftest.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_assign.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_assign_rows.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_assign_sequence.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_combine.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_combine_columns.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_combine_rows.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_extract_column_names.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_extract_row_names.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_get_height.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_intersect.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_is_high_dimensional.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_list_type_checks.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_map_to_index.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_match.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_normalize_subscript.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_package_utils.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_print_truncated.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_print_wrapped_table.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_show_as_cell.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_subset.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_subset_rows.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_subset_sequence.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_union.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tests/test_which.py +0 -0
- {biocutils-0.3.1.dev0 → biocutils-0.3.2}/tox.ini +0 -0
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## Version 0.3.0
|
|
3
|
+
## Version 0.3.0 - 0.3.2
|
|
4
4
|
|
|
5
5
|
- Provide a base `BiocObject` class similar to the `Annotated` class in Bioconductor. The class provides `metadata` slot, accessors and validation functions.
|
|
6
|
+
- Renaming code files to follow pep guidelines
|
|
7
|
+
- Update Github actions and workflow to the new biocsetup versions
|
|
8
|
+
- Changes to improve `NamedList`, `Names` classes
|
|
9
|
+
- get name at index
|
|
10
|
+
- delete method for namedlist/names
|
|
11
|
+
- add is_unique
|
|
12
|
+
- add lint errors
|
|
13
|
+
- linting documentation, typehints etc
|
|
6
14
|
|
|
7
15
|
## Version 0.2.3
|
|
8
16
|
|
|
@@ -424,8 +424,8 @@ class Factor:
|
|
|
424
424
|
output._codes[index] = -1
|
|
425
425
|
return output
|
|
426
426
|
|
|
427
|
-
for i,
|
|
428
|
-
if
|
|
427
|
+
for i, lev in enumerate(output._levels):
|
|
428
|
+
if lev == value:
|
|
429
429
|
output._codes[index] = i
|
|
430
430
|
return output
|
|
431
431
|
|
|
@@ -764,6 +764,60 @@ class Factor:
|
|
|
764
764
|
levels, indices = factorize(x, levels=levels, sort_levels=sort_levels, **kwargs)
|
|
765
765
|
return Factor(indices, levels=levels, ordered=ordered, names=names)
|
|
766
766
|
|
|
767
|
+
################################
|
|
768
|
+
#####>>>> List methods <<<<#####
|
|
769
|
+
################################
|
|
770
|
+
|
|
771
|
+
def as_list(self) -> list:
|
|
772
|
+
"""
|
|
773
|
+
Returns:
|
|
774
|
+
List of strings corresponding to the factor elements.
|
|
775
|
+
Missing values are represented as None.
|
|
776
|
+
"""
|
|
777
|
+
return [self._levels[c] if c >= 0 else None for c in self._codes]
|
|
778
|
+
|
|
779
|
+
def safe_delete(self, index: Union[int, str, slice], in_place: bool = False) -> Factor:
|
|
780
|
+
"""
|
|
781
|
+
Args:
|
|
782
|
+
index:
|
|
783
|
+
Integer index or slice containing position(s) to delete.
|
|
784
|
+
Alternatively, the name of the value to delete (the first
|
|
785
|
+
occurrence of the name is used).
|
|
786
|
+
|
|
787
|
+
in_place:
|
|
788
|
+
Whether to modify the current object in place.
|
|
789
|
+
|
|
790
|
+
Returns:
|
|
791
|
+
A ``Factor`` where the item at ``index`` is removed. This is a
|
|
792
|
+
new object if ``in_place = False``, otherwise it is a reference to
|
|
793
|
+
the current object.
|
|
794
|
+
"""
|
|
795
|
+
if in_place:
|
|
796
|
+
output = self
|
|
797
|
+
else:
|
|
798
|
+
output = copy(self)
|
|
799
|
+
output._codes = copy(self._codes)
|
|
800
|
+
if output._names is not None:
|
|
801
|
+
output._names = output._names.copy()
|
|
802
|
+
|
|
803
|
+
if isinstance(index, str):
|
|
804
|
+
index = _name_to_position(output._names, index)
|
|
805
|
+
|
|
806
|
+
output._codes = numpy.delete(output._codes, index)
|
|
807
|
+
|
|
808
|
+
if output._names is not None:
|
|
809
|
+
output._names.delete(index)
|
|
810
|
+
|
|
811
|
+
return output
|
|
812
|
+
|
|
813
|
+
def delete(self, index: Union[int, str, slice]):
|
|
814
|
+
"""Alias for :py:meth:`~safe_delete` with ``in_place = True``."""
|
|
815
|
+
self.safe_delete(index, in_place=True)
|
|
816
|
+
|
|
817
|
+
def __delitem__(self, index: Union[int, str, slice]):
|
|
818
|
+
"""Alias for :py:meth:`~delete`."""
|
|
819
|
+
self.delete(index)
|
|
820
|
+
|
|
767
821
|
|
|
768
822
|
@subset_sequence.register
|
|
769
823
|
def _subset_sequence_Factor(x: Factor, indices: Sequence[int]) -> Factor:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from copy import deepcopy
|
|
4
|
-
from typing import Any, Dict, Iterable, Optional, Sequence, Union
|
|
4
|
+
from typing import Any, Dict, Iterable, Optional, Sequence, Tuple, Union
|
|
5
5
|
|
|
6
6
|
from .assign_sequence import assign_sequence
|
|
7
7
|
from .combine_sequences import combine_sequences
|
|
@@ -153,12 +153,27 @@ class NamedList:
|
|
|
153
153
|
output._names = _sanitize_names(names, len(self))
|
|
154
154
|
return output
|
|
155
155
|
|
|
156
|
+
def get_name(self, index: int) -> Optional[str]:
|
|
157
|
+
"""Get name at an index.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
index:
|
|
161
|
+
Integer index of the element.
|
|
162
|
+
Returns:
|
|
163
|
+
Names for the list elements.
|
|
164
|
+
"""
|
|
165
|
+
if self._names is None:
|
|
166
|
+
return None
|
|
167
|
+
|
|
168
|
+
return self._names.get_value(index)
|
|
169
|
+
|
|
156
170
|
#################################
|
|
157
171
|
#####>>>> Get/set items <<<<#####
|
|
158
172
|
#################################
|
|
159
173
|
|
|
160
174
|
def get_value(self, index: Union[str, int]) -> Any:
|
|
161
|
-
"""
|
|
175
|
+
"""Get value at an index.
|
|
176
|
+
|
|
162
177
|
Args:
|
|
163
178
|
index:
|
|
164
179
|
Integer index of the element to obtain. Alternatively, a string
|
|
@@ -420,6 +435,95 @@ class NamedList:
|
|
|
420
435
|
self.extend(other)
|
|
421
436
|
return self
|
|
422
437
|
|
|
438
|
+
def safe_delete(self, index: Union[int, str, slice], in_place: bool = False) -> NamedList:
|
|
439
|
+
"""
|
|
440
|
+
Args:
|
|
441
|
+
index:
|
|
442
|
+
An integer index or slice containing position(s) to delete.
|
|
443
|
+
Alternatively, the name of the value to delete (the first
|
|
444
|
+
occurrence of the name is used).
|
|
445
|
+
|
|
446
|
+
in_place:
|
|
447
|
+
Whether to modify the current object in place.
|
|
448
|
+
|
|
449
|
+
Returns:
|
|
450
|
+
A ``NamedList`` where the item at ``index`` is removed. This is a
|
|
451
|
+
new object if ``in_place = False``, otherwise it is a reference to
|
|
452
|
+
the current object.
|
|
453
|
+
"""
|
|
454
|
+
if in_place:
|
|
455
|
+
output = self
|
|
456
|
+
else:
|
|
457
|
+
output = self._shallow_copy()
|
|
458
|
+
output._data = output._data[:] # Shallow copy of the list
|
|
459
|
+
if output._names is not None:
|
|
460
|
+
output._names = output._names.copy()
|
|
461
|
+
|
|
462
|
+
if isinstance(index, str):
|
|
463
|
+
index = _name_to_position(self._names, index)
|
|
464
|
+
|
|
465
|
+
del output._data[index]
|
|
466
|
+
if output._names is not None:
|
|
467
|
+
output._names.delete(index)
|
|
468
|
+
|
|
469
|
+
return output
|
|
470
|
+
|
|
471
|
+
def delete(self, index: Union[int, str, slice]):
|
|
472
|
+
"""Alias for :py:meth:`~safe_delete` with ``in_place = True``."""
|
|
473
|
+
self.safe_delete(index, in_place=True)
|
|
474
|
+
|
|
475
|
+
def __delitem__(self, index: Union[int, str, slice]):
|
|
476
|
+
"""Alias for :py:meth:`~delete`."""
|
|
477
|
+
self.delete(index)
|
|
478
|
+
|
|
479
|
+
#####################################
|
|
480
|
+
#####>>>> dict like methods <<<<#####
|
|
481
|
+
#####################################
|
|
482
|
+
|
|
483
|
+
def keys(self) -> Iterable[str]:
|
|
484
|
+
"""
|
|
485
|
+
Returns:
|
|
486
|
+
Iterator over the names of the list elements.
|
|
487
|
+
"""
|
|
488
|
+
if self._names is None:
|
|
489
|
+
return iter([])
|
|
490
|
+
return iter(self._names)
|
|
491
|
+
|
|
492
|
+
def values(self) -> Iterable[Any]:
|
|
493
|
+
"""
|
|
494
|
+
Returns:
|
|
495
|
+
Iterator over the values of the list elements.
|
|
496
|
+
"""
|
|
497
|
+
return iter(self._data)
|
|
498
|
+
|
|
499
|
+
def items(self) -> Iterable[Tuple[str, Any]]:
|
|
500
|
+
"""
|
|
501
|
+
Returns:
|
|
502
|
+
Iterator over (name, value) pairs.
|
|
503
|
+
If names are missing, keys are returned as stringified indices.
|
|
504
|
+
"""
|
|
505
|
+
if self._names is not None:
|
|
506
|
+
return zip(self._names, self._data)
|
|
507
|
+
else:
|
|
508
|
+
return zip((str(i) for i in range(len(self))), self._data)
|
|
509
|
+
|
|
510
|
+
def get(self, key: Union[str, int], default: Any = None) -> Any:
|
|
511
|
+
"""
|
|
512
|
+
Args:
|
|
513
|
+
key:
|
|
514
|
+
Name or index of the element.
|
|
515
|
+
|
|
516
|
+
default:
|
|
517
|
+
Value to return if ``key`` is not found.
|
|
518
|
+
|
|
519
|
+
Returns:
|
|
520
|
+
Value at ``key`` or ``default``.
|
|
521
|
+
"""
|
|
522
|
+
try:
|
|
523
|
+
return self.get_value(key)
|
|
524
|
+
except (KeyError, IndexError):
|
|
525
|
+
return default
|
|
526
|
+
|
|
423
527
|
################################
|
|
424
528
|
#####>>>> Copy methods <<<<#####
|
|
425
529
|
################################
|
|
@@ -121,6 +121,17 @@ class Names:
|
|
|
121
121
|
else:
|
|
122
122
|
return -1
|
|
123
123
|
|
|
124
|
+
def __contains__(self, name: str) -> bool:
|
|
125
|
+
"""
|
|
126
|
+
Args:
|
|
127
|
+
name:
|
|
128
|
+
Name to check.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
True if ``name`` exists, otherwise False.
|
|
132
|
+
"""
|
|
133
|
+
return self.map(name) >= 0
|
|
134
|
+
|
|
124
135
|
#################################
|
|
125
136
|
#####>>>> Get/set items <<<<#####
|
|
126
137
|
#################################
|
|
@@ -328,6 +339,35 @@ class Names:
|
|
|
328
339
|
self.extend(other)
|
|
329
340
|
return self
|
|
330
341
|
|
|
342
|
+
def safe_delete(self, index: Union[int, slice], in_place: bool = False) -> Names:
|
|
343
|
+
"""
|
|
344
|
+
Args:
|
|
345
|
+
index:
|
|
346
|
+
Position(s) of the name(s) to delete.
|
|
347
|
+
|
|
348
|
+
in_place:
|
|
349
|
+
Whether to perform this deletion in-place.
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
A ``Names`` object with the deleted name(s). This is a new object
|
|
353
|
+
if ``in_place = False``, otherwise it is a reference to the current
|
|
354
|
+
object.
|
|
355
|
+
"""
|
|
356
|
+
output = self._define_output(in_place)
|
|
357
|
+
if in_place:
|
|
358
|
+
output._wipe_reverse_index()
|
|
359
|
+
|
|
360
|
+
del output._names[index]
|
|
361
|
+
return output
|
|
362
|
+
|
|
363
|
+
def delete(self, index: Union[int, slice]):
|
|
364
|
+
"""Alias for :py:attr:`~safe_delete` with ``in_place = True``."""
|
|
365
|
+
self.safe_delete(index, in_place=True)
|
|
366
|
+
|
|
367
|
+
def __delitem__(self, index: Union[int, slice]):
|
|
368
|
+
"""Alias for :py:attr:`~delete`."""
|
|
369
|
+
self.delete(index)
|
|
370
|
+
|
|
331
371
|
################################
|
|
332
372
|
#####>>>> Copy methods <<<<#####
|
|
333
373
|
################################
|
|
@@ -359,6 +399,15 @@ class Names:
|
|
|
359
399
|
"""
|
|
360
400
|
return type(self)(deepcopy(self._names, memo, _nil), _validate=False)
|
|
361
401
|
|
|
402
|
+
@property
|
|
403
|
+
def is_unique(self) -> bool:
|
|
404
|
+
"""
|
|
405
|
+
Returns:
|
|
406
|
+
True if all names are unique, otherwise False.
|
|
407
|
+
"""
|
|
408
|
+
self._populate_reverse_index()
|
|
409
|
+
return len(self._reverse) == len(self._names)
|
|
410
|
+
|
|
362
411
|
|
|
363
412
|
@subset_sequence.register
|
|
364
413
|
def _subset_sequence_Names(x: Names, indices: Sequence[int]) -> Names:
|
|
@@ -44,7 +44,7 @@ def test_BooleanList_setitem():
|
|
|
44
44
|
x = BooleanList([False, True, True, False])
|
|
45
45
|
x[0] = None
|
|
46
46
|
assert x.as_list() == [None, True, True, False]
|
|
47
|
-
x[0] = 12345
|
|
47
|
+
x[0] = 12345
|
|
48
48
|
assert x.as_list() == [True, True, True, False]
|
|
49
49
|
|
|
50
50
|
x[1:3] = [False, False]
|
|
@@ -54,7 +54,7 @@ def test_BooleanList_setitem():
|
|
|
54
54
|
assert x.as_list() == [None, False, None, False]
|
|
55
55
|
|
|
56
56
|
x.set_names(["A", "B", "C", "D"], in_place=True)
|
|
57
|
-
x["C"] = True
|
|
57
|
+
x["C"] = True
|
|
58
58
|
assert x.as_list() == [None, False, True, False]
|
|
59
59
|
x[["A", "B"]] = [False, True]
|
|
60
60
|
assert x.as_list() == [False, True, True, False]
|
|
@@ -17,13 +17,13 @@ def test_factor_init():
|
|
|
17
17
|
assert len(f) == 6
|
|
18
18
|
assert list(f) == ["A", "B", None, "A", None, "E"]
|
|
19
19
|
assert list(f.get_codes()) == [0, 1, -1, 0, -1, 4]
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
f = Factor([None] * 10, levels=["A", "B", "C", "D", "E"])
|
|
22
22
|
assert list(f) == [None] * 10
|
|
23
23
|
|
|
24
24
|
# Works with NumPy inputs.
|
|
25
25
|
f = Factor(numpy.array([4,3,2,1,0], dtype=numpy.uint8), levels=numpy.array(["A", "B", "C", "D", "E"]))
|
|
26
|
-
assert len(f) == 5
|
|
26
|
+
assert len(f) == 5
|
|
27
27
|
assert f.get_codes().dtype == numpy.int8
|
|
28
28
|
assert isinstance(f.get_levels(), StringList)
|
|
29
29
|
|
|
@@ -98,7 +98,7 @@ def test_Factor_get_value():
|
|
|
98
98
|
def test_Factor_get_slice():
|
|
99
99
|
f = Factor([0, 1, 2, -1, 2, 4], levels=["A", "B", "C", "D", "E"])
|
|
100
100
|
|
|
101
|
-
sub = f.get_slice([0, 1])
|
|
101
|
+
sub = f.get_slice([0, 1])
|
|
102
102
|
assert list(sub) == ["A", "B"]
|
|
103
103
|
assert sub.get_levels() == f.get_levels()
|
|
104
104
|
|
|
@@ -176,7 +176,7 @@ def test_Factor_setitem():
|
|
|
176
176
|
f[-1] = "D"
|
|
177
177
|
assert list(f.get_codes()) == [1, 1, 0, 0, 2, 3]
|
|
178
178
|
|
|
179
|
-
f[2:5] = Factor([4, 3, 1], levels=["A", "B", "C", "D", "E"])
|
|
179
|
+
f[2:5] = Factor([4, 3, 1], levels=["A", "B", "C", "D", "E"])
|
|
180
180
|
assert list(f.get_codes()) == [1, 1, 4, 3, 1, 3]
|
|
181
181
|
assert f.get_levels() == f.get_levels()
|
|
182
182
|
|
|
@@ -339,3 +339,54 @@ def test_Factor_init_from_list():
|
|
|
339
339
|
assert isinstance(f1, Factor)
|
|
340
340
|
assert len(f1) == 5
|
|
341
341
|
assert len(f1.get_levels()) == 3
|
|
342
|
+
|
|
343
|
+
def test_Factor_as_list():
|
|
344
|
+
f = Factor([0, 1, -1, 0], levels=["A", "B"])
|
|
345
|
+
assert f.as_list() == ["A", "B", None, "A"]
|
|
346
|
+
|
|
347
|
+
empty = Factor([], levels=[])
|
|
348
|
+
assert empty.as_list() == []
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def test_Factor_safe_delete():
|
|
352
|
+
f = Factor([0, 1, 2, 0], levels=["A", "B", "C"], names=["x", "y", "z", "w"])
|
|
353
|
+
|
|
354
|
+
y = f.safe_delete(1)
|
|
355
|
+
assert y.as_list() == ["A", "C", "A"]
|
|
356
|
+
assert y.get_names().as_list() == ["x", "z", "w"]
|
|
357
|
+
assert f.as_list() == ["A", "B", "C", "A"]
|
|
358
|
+
|
|
359
|
+
y = f.safe_delete("y")
|
|
360
|
+
assert y.as_list() == ["A", "C", "A"]
|
|
361
|
+
assert y.get_names().as_list() == ["x", "z", "w"]
|
|
362
|
+
|
|
363
|
+
y = f.safe_delete(slice(1, 3))
|
|
364
|
+
assert y.as_list() == ["A", "A"]
|
|
365
|
+
assert y.get_names().as_list() == ["x", "w"]
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def test_Factor_delete():
|
|
369
|
+
f = Factor([0, 1, 2], levels=["A", "B", "C"], names=["x", "y", "z"])
|
|
370
|
+
|
|
371
|
+
f.delete(1)
|
|
372
|
+
assert f.as_list() == ["A", "C"]
|
|
373
|
+
assert f.get_names().as_list() == ["x", "z"]
|
|
374
|
+
|
|
375
|
+
f.delete("z")
|
|
376
|
+
assert f.as_list() == ["A"]
|
|
377
|
+
assert f.get_names().as_list() == ["x"]
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def test_Factor_delitem():
|
|
381
|
+
f = Factor([0, 1, 2, 0], levels=["A", "B", "C"], names=["x", "y", "z", "w"])
|
|
382
|
+
|
|
383
|
+
del f["y"]
|
|
384
|
+
assert f.as_list() == ["A", "C", "A"]
|
|
385
|
+
assert f.get_names().as_list() == ["x", "z", "w"]
|
|
386
|
+
|
|
387
|
+
del f[0]
|
|
388
|
+
assert f.as_list() == ["C", "A"]
|
|
389
|
+
assert f.get_names().as_list() == ["z", "w"]
|
|
390
|
+
|
|
391
|
+
del f[:]
|
|
392
|
+
assert len(f) == 0
|
|
@@ -10,6 +10,7 @@ def test_NamedList_init():
|
|
|
10
10
|
assert x.as_list() == [ 1,2,3,4 ]
|
|
11
11
|
assert x.get_names().as_list() == ["a", "b", "c", "d"]
|
|
12
12
|
assert len(x) == 4
|
|
13
|
+
assert x.get_name(0) == "a"
|
|
13
14
|
|
|
14
15
|
y = NamedList(x)
|
|
15
16
|
assert y.as_list() == [1,2,3,4]
|
|
@@ -23,6 +24,7 @@ def test_NamedList_init():
|
|
|
23
24
|
x = NamedList([1,2,3,4])
|
|
24
25
|
assert x.as_list() == [1,2,3,4]
|
|
25
26
|
assert x.get_names() is None
|
|
27
|
+
assert x.get_name(1) is None
|
|
26
28
|
|
|
27
29
|
|
|
28
30
|
def test_Names_iter():
|
|
@@ -255,3 +257,76 @@ def test_NamedList_generics():
|
|
|
255
257
|
y = biocutils.assign_sequence(x, [1, 3], NamedList([ 20, 40 ], names=["b", "d" ]))
|
|
256
258
|
assert y.as_list() == [ 1, 20, 3, 40 ]
|
|
257
259
|
assert y.get_names().as_list() == [ "A", "B", "C", "D" ] # doesn't set the names, as per policy.
|
|
260
|
+
|
|
261
|
+
def test_NamedList_safe_delete():
|
|
262
|
+
x = NamedList([1, 2, 3, 4], names=["A", "B", "C", "D"])
|
|
263
|
+
|
|
264
|
+
y = x.safe_delete(1)
|
|
265
|
+
assert y.as_list() == [1, 3, 4]
|
|
266
|
+
assert y.get_names().as_list() == ["A", "C", "D"]
|
|
267
|
+
assert x.as_list() == [1, 2, 3, 4]
|
|
268
|
+
|
|
269
|
+
y = x.safe_delete("C")
|
|
270
|
+
assert y.as_list() == [1, 2, 4]
|
|
271
|
+
assert y.get_names().as_list() == ["A", "B", "D"]
|
|
272
|
+
|
|
273
|
+
y = x.safe_delete(slice(1, 3))
|
|
274
|
+
assert y.as_list() == [1, 4]
|
|
275
|
+
assert y.get_names().as_list() == ["A", "D"]
|
|
276
|
+
|
|
277
|
+
y = x.safe_delete(-1)
|
|
278
|
+
assert y.as_list() == [1, 2, 3]
|
|
279
|
+
assert y.get_names().as_list() == ["A", "B", "C"]
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def test_NamedList_delete():
|
|
283
|
+
x = NamedList([1, 2, 3, 4], names=["A", "B", "C", "D"])
|
|
284
|
+
|
|
285
|
+
x.delete(0)
|
|
286
|
+
assert x.as_list() == [2, 3, 4]
|
|
287
|
+
assert x.get_names().as_list() == ["B", "C", "D"]
|
|
288
|
+
|
|
289
|
+
x.delete("D")
|
|
290
|
+
assert x.as_list() == [2, 3]
|
|
291
|
+
assert x.get_names().as_list() == ["B", "C"]
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def test_NamedList_delitem():
|
|
295
|
+
x = NamedList([1, 2, 3, 4], names=["A", "B", "C", "D"])
|
|
296
|
+
|
|
297
|
+
del x[1]
|
|
298
|
+
assert x.as_list() == [1, 3, 4]
|
|
299
|
+
assert x.get_names().as_list() == ["A", "C", "D"]
|
|
300
|
+
|
|
301
|
+
del x["A"]
|
|
302
|
+
assert x.as_list() == [3, 4]
|
|
303
|
+
assert x.get_names().as_list() == ["C", "D"]
|
|
304
|
+
|
|
305
|
+
x = NamedList([1, 2, 3, 4], names=["A", "B", "C", "D"])
|
|
306
|
+
del x[0:2]
|
|
307
|
+
assert x.as_list() == [3, 4]
|
|
308
|
+
assert x.get_names().as_list() == ["C", "D"]
|
|
309
|
+
|
|
310
|
+
with pytest.raises(KeyError):
|
|
311
|
+
del x["Missing"]
|
|
312
|
+
|
|
313
|
+
with pytest.raises(IndexError):
|
|
314
|
+
del x[10]
|
|
315
|
+
|
|
316
|
+
def test_NamedList_dict_methods():
|
|
317
|
+
x = NamedList([1, 2, 3], names=["A", "B", "C"])
|
|
318
|
+
|
|
319
|
+
assert list(x.keys()) == ["A", "B", "C"]
|
|
320
|
+
assert list(x.values()) == [1, 2, 3]
|
|
321
|
+
assert list(x.items()) == [("A", 1), ("B", 2), ("C", 3)]
|
|
322
|
+
|
|
323
|
+
assert x.get("A") == 1
|
|
324
|
+
assert x.get("C") == 3
|
|
325
|
+
assert x.get("Missing") is None
|
|
326
|
+
assert x.get("Missing", 100) == 100
|
|
327
|
+
assert x.get(1) == 2 # Integer index access via get
|
|
328
|
+
|
|
329
|
+
y = NamedList([10, 20])
|
|
330
|
+
assert list(y.keys()) == []
|
|
331
|
+
assert list(y.values()) == [10, 20]
|
|
332
|
+
assert list(y.items()) == [("0", 10), ("1", 20)]
|
|
@@ -185,7 +185,7 @@ def test_Names_generics():
|
|
|
185
185
|
sub = biocutils.subset_sequence(x, [0,3,2,1])
|
|
186
186
|
assert isinstance(sub, Names)
|
|
187
187
|
assert sub.as_list() == ["1", "4", "3", "2"]
|
|
188
|
-
|
|
188
|
+
|
|
189
189
|
y = ["a", "b", "c", "d"]
|
|
190
190
|
com = biocutils.combine_sequences(x, y)
|
|
191
191
|
assert isinstance(com, Names)
|
|
@@ -196,3 +196,64 @@ def test_Names_generics():
|
|
|
196
196
|
assert isinstance(ass, Names)
|
|
197
197
|
assert ass.as_list() == ["1", "b", "c", "4"]
|
|
198
198
|
|
|
199
|
+
def test_Names_safe_delete():
|
|
200
|
+
x = Names(["A", "B", "C", "D"])
|
|
201
|
+
|
|
202
|
+
y = x.safe_delete(1)
|
|
203
|
+
assert y.as_list() == ["A", "C", "D"]
|
|
204
|
+
assert y.map("B") == -1
|
|
205
|
+
assert y.map("C") == 1
|
|
206
|
+
assert x.as_list() == ["A", "B", "C", "D"]
|
|
207
|
+
|
|
208
|
+
y = x.safe_delete(slice(0, 2))
|
|
209
|
+
assert y.as_list() == ["C", "D"]
|
|
210
|
+
assert y.map("A") == -1
|
|
211
|
+
assert y.map("C") == 0
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def test_Names_delete():
|
|
215
|
+
x = Names(["A", "B", "C", "D"])
|
|
216
|
+
|
|
217
|
+
x.delete(2)
|
|
218
|
+
assert x.as_list() == ["A", "B", "D"]
|
|
219
|
+
assert x.map("C") == -1
|
|
220
|
+
assert x.map("D") == 2
|
|
221
|
+
|
|
222
|
+
x.delete(0)
|
|
223
|
+
assert x.as_list() == ["B", "D"]
|
|
224
|
+
assert x.map("A") == -1
|
|
225
|
+
assert x.map("B") == 0
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def test_Names_delitem():
|
|
229
|
+
x = Names(["1", "2", "3", "4"])
|
|
230
|
+
|
|
231
|
+
del x[1]
|
|
232
|
+
assert x.as_list() == ["1", "3", "4"]
|
|
233
|
+
assert x.map("2") == -1
|
|
234
|
+
assert x.map("3") == 1
|
|
235
|
+
|
|
236
|
+
del x[0:2]
|
|
237
|
+
assert x.as_list() == ["4"]
|
|
238
|
+
assert x.map("1") == -1
|
|
239
|
+
assert x.map("4") == 0
|
|
240
|
+
|
|
241
|
+
def test_Names_contains():
|
|
242
|
+
x = Names(["A", "B", "C"])
|
|
243
|
+
assert "A" in x
|
|
244
|
+
assert "B" in x
|
|
245
|
+
assert "Z" not in x
|
|
246
|
+
|
|
247
|
+
# Works with duplicates
|
|
248
|
+
y = Names(["A", "A", "B"])
|
|
249
|
+
assert "A" in y
|
|
250
|
+
|
|
251
|
+
def test_Names_is_unique():
|
|
252
|
+
x = Names(["A", "B", "C"])
|
|
253
|
+
assert x.is_unique
|
|
254
|
+
|
|
255
|
+
y = Names(["A", "B", "A"])
|
|
256
|
+
assert not y.is_unique
|
|
257
|
+
|
|
258
|
+
empty = Names([])
|
|
259
|
+
assert empty.is_unique
|
|
@@ -44,7 +44,7 @@ def test_StringList_setitem():
|
|
|
44
44
|
x = StringList([1,2,3,4])
|
|
45
45
|
x[0] = None
|
|
46
46
|
assert x.as_list() == [None, "2", "3", "4"]
|
|
47
|
-
x[0] = 12345
|
|
47
|
+
x[0] = 12345
|
|
48
48
|
assert x.as_list() == ["12345", "2", "3", "4"]
|
|
49
49
|
|
|
50
50
|
x[1:3] = [10, 20]
|
|
@@ -89,7 +89,7 @@ def test_StringList_generics():
|
|
|
89
89
|
sub = biocutils.subset_sequence(x, [0,3,2,1])
|
|
90
90
|
assert isinstance(sub, StringList)
|
|
91
91
|
assert sub.as_list() == ["1", "4", "3", "2"]
|
|
92
|
-
|
|
92
|
+
|
|
93
93
|
y = ["a", "b", "c", "d"]
|
|
94
94
|
com = biocutils.combine_sequences(x, y)
|
|
95
95
|
assert isinstance(com, StringList)
|
|
@@ -3,7 +3,7 @@ from copy import copy
|
|
|
3
3
|
from biocutils.bioc_object import BiocObject
|
|
4
4
|
from biocutils.named_list import NamedList
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
def test_init_empty():
|
|
8
8
|
"""Test initialization with default values."""
|
|
9
9
|
obj = BiocObject()
|
|
@@ -14,7 +14,7 @@ def test_init_with_dict():
|
|
|
14
14
|
"""Test initialization with a dictionary."""
|
|
15
15
|
meta = {"author": "jkanche", "version": 1}
|
|
16
16
|
obj = BiocObject(metadata=meta)
|
|
17
|
-
|
|
17
|
+
|
|
18
18
|
assert isinstance(obj.metadata, NamedList)
|
|
19
19
|
assert len(obj.metadata) == 2
|
|
20
20
|
|
|
@@ -28,7 +28,7 @@ def test_metadata_property_setter():
|
|
|
28
28
|
obj = BiocObject()
|
|
29
29
|
new_meta = {"tag": "experiment_1"}
|
|
30
30
|
obj.metadata = new_meta
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
assert len(obj.metadata) == 1
|
|
33
33
|
assert isinstance(obj.metadata, NamedList)
|
|
34
34
|
|
|
@@ -36,21 +36,21 @@ def test_set_metadata_copy():
|
|
|
36
36
|
"""Test functional style set_metadata (copy-on-write)."""
|
|
37
37
|
obj = BiocObject(metadata={"id": 1})
|
|
38
38
|
original_id = id(obj)
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
new_obj = obj.set_metadata({"id": 2})
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
assert id(new_obj) != original_id
|
|
43
43
|
assert len(new_obj.metadata) == 1
|
|
44
|
-
|
|
45
|
-
assert len(obj.metadata) == 1
|
|
44
|
+
|
|
45
|
+
assert len(obj.metadata) == 1
|
|
46
46
|
|
|
47
47
|
def test_set_metadata_inplace():
|
|
48
48
|
"""Test imperative style set_metadata (in-place)."""
|
|
49
49
|
obj = BiocObject(metadata={"id": 1})
|
|
50
50
|
original_id = id(obj)
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
new_obj = obj.set_metadata({"id": 2}, in_place=True)
|
|
53
|
-
|
|
53
|
+
|
|
54
54
|
assert id(new_obj) == original_id
|
|
55
55
|
assert new_obj is obj
|
|
56
56
|
assert len(obj.metadata) == 1
|
|
@@ -59,18 +59,18 @@ def test_inheritance():
|
|
|
59
59
|
"""Test that subclasses maintain their type when copying."""
|
|
60
60
|
class GenomicContainer(BiocObject):
|
|
61
61
|
pass
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
obj = GenomicContainer(metadata={"genome": "hg38"})
|
|
64
64
|
new_obj = obj.set_metadata({"genome": "mm10"})
|
|
65
|
-
|
|
65
|
+
|
|
66
66
|
assert isinstance(new_obj, GenomicContainer)
|
|
67
67
|
assert new_obj is not obj
|
|
68
68
|
|
|
69
69
|
def test_shallow_copy_behavior():
|
|
70
70
|
heavy_data = ["large", "data"]
|
|
71
|
-
|
|
71
|
+
|
|
72
72
|
obj = BiocObject()
|
|
73
73
|
obj._heavy_data = heavy_data
|
|
74
74
|
new_obj = obj.set_metadata({"new": "meta"})
|
|
75
75
|
assert new_obj is not obj
|
|
76
|
-
assert new_obj._heavy_data is obj._heavy_data
|
|
76
|
+
assert new_obj._heavy_data is obj._heavy_data
|
|
@@ -52,7 +52,7 @@ def test_factorize_sorted():
|
|
|
52
52
|
def test_factorize_factor():
|
|
53
53
|
f = Factor([4, 3, 2, 1, 0], ["A", "B", "C", "D", "E"])
|
|
54
54
|
lev, ind = factorize(f)
|
|
55
|
-
assert lev == ["E", "D", "C", "B", "A"]
|
|
55
|
+
assert lev == ["E", "D", "C", "B", "A"]
|
|
56
56
|
assert list(ind) == [0, 1, 2, 3, 4]
|
|
57
57
|
|
|
58
58
|
lev, ind = factorize(f, sort_levels=True)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|