Skip to content

Commit e960084

Browse files
committed
Merge branch 'main' into poly-seq-scheme
Get changes to ReplaceCategoryFilter and support for the ihm_entry_collection_mapping table.
2 parents a20d757 + 5a8366d commit e960084

9 files changed

Lines changed: 134 additions & 13 deletions

File tree

ihm/__init__.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1908,11 +1908,20 @@ class Collection:
19081908
:param str id: Unique identifier (assigned by the archive).
19091909
:param str name: Short name for the collection.
19101910
:param str details: Longer description of the collection.
1911+
:param list entries: Explicit list of entry IDs in this collection;
1912+
if empty, just the current entry is assumed.
19111913
19121914
See also :attr:`System.collections`.
19131915
"""
1914-
def __init__(self, id, name=None, details=None):
1916+
def __init__(self, id, name=None, details=None, entries=None):
19151917
self.id, self.name, self.details = id, name, details
1918+
self.entries = entries or []
1919+
1920+
def __set_id(self, val):
1921+
self.id = val
1922+
1923+
# SystemReader wants to set _id, not id
1924+
_id = property(lambda s: s.id, __set_id)
19161925

19171926

19181927
class BranchDescriptor:

ihm/dumper.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,22 @@ def dump(self, system, writer):
9292

9393
class _CollectionDumper(Dumper):
9494
def dump(self, system, writer):
95+
self.dump_summary(system, writer)
96+
self.dump_mapping(system, writer)
97+
98+
def dump_summary(self, system, writer):
9599
with writer.loop("_ihm_entry_collection",
96100
["id", "name", "details"]) as lp:
97101
for c in system.collections:
98102
lp.write(id=c.id, name=c.name, details=c.details)
99103

104+
def dump_mapping(self, system, writer):
105+
with writer.loop("_ihm_entry_collection_mapping",
106+
["collection_id", "entry_id"]) as lp:
107+
for c in system.collections:
108+
for eid in (c.entries or [system.id]):
109+
lp.write(collection_id=c.id, entry_id=eid)
110+
100111

101112
class _AuditConformDumper(Dumper):
102113
URL = ("https://raw.githubusercontent.com/" +

ihm/format.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -893,7 +893,13 @@ def _get_replacement_token(self):
893893
writer = CifWriter(fh)
894894
self.dumper.finalize(self.system)
895895
self.dumper.dump(self.system, writer)
896-
return self._RawCifToken(fh.getvalue())
896+
cif = fh.getvalue()
897+
# Strip _CifLoopWriter prefix and suffix, if any
898+
if cif.startswith('#\n'):
899+
cif = cif[2:]
900+
if cif.endswith('#\n'):
901+
cif = cif[:-2]
902+
return self._RawCifToken(cif)
897903

898904
def filter_category(self, tok):
899905
if self.match_token_category(tok):

ihm/reader.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,10 @@ def __init__(self, model_class, starting_model_class, system=None):
481481
self.revisions = IDMapper(self.system.revisions, ihm.Revision,
482482
*(None,) * 4)
483483

484+
#: Mapping from ID to :class:`ihm.Collection` objects
485+
self.collections = IDMapper(self.system.collections, ihm.Collection,
486+
None)
487+
484488
#: Mapping from ID to :class:`ihm.Entity` objects
485489
self.entities = IDMapper(self.system.entities, _make_new_entity)
486490

@@ -1060,8 +1064,17 @@ class _CollectionHandler(Handler):
10601064
category = '_ihm_entry_collection'
10611065

10621066
def __call__(self, id, name, details):
1063-
c = ihm.Collection(id=id, name=name, details=details)
1064-
self.system.collections.append(c)
1067+
c = self.sysr.collections.get_by_id(id)
1068+
c.name = name
1069+
c.details = details
1070+
1071+
1072+
class _CollectionMappingHandler(Handler):
1073+
category = '_ihm_entry_collection_mapping'
1074+
1075+
def __call__(self, collection_id, entry_id):
1076+
c = self.sysr.collections.get_by_id(collection_id)
1077+
c.entries.append(entry_id)
10651078

10661079

10671080
class _StructHandler(Handler):
@@ -4154,7 +4167,8 @@ class IHMVariant(Variant):
41544167
system_reader = SystemReader
41554168

41564169
_handlers = [
4157-
_CollectionHandler, _StructHandler, _SoftwareHandler, _CitationHandler,
4170+
_CollectionHandler, _CollectionMappingHandler, _StructHandler,
4171+
_SoftwareHandler, _CitationHandler,
41584172
_DatabaseHandler, _DatabaseStatusHandler,
41594173
_AuditAuthorHandler, _AuditRevisionHistoryHandler,
41604174
_AuditRevisionDetailsHandler, _AuditRevisionGroupHandler,

setup.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,20 @@
1717
build_ext = False
1818
copy_args.remove('--without-ext')
1919

20+
# Allow building with Python limited API
21+
if '--py_limited_api' in copy_args:
22+
copy_args.remove('--py_limited_api')
23+
# We require Python 3.11 or later since we use PyBUF_WRITE which only
24+
# became part of the stable API in 3.11
25+
ext_limited_args = {
26+
'define_macros': [("Py_LIMITED_API", "0x030B0000")],
27+
'py_limited_api': True}
28+
setup_limited_args = {
29+
'options': {"bdist_wheel": {"py_limited_api": "cp311"}}}
30+
else:
31+
ext_limited_args = {}
32+
setup_limited_args = {}
33+
2034
if sys.platform == 'win32':
2135
# Our use of strdup, strerror should be safe - no need for the Windows
2236
# compiler to warn about it; we want to use the POSIX name for strdup too
@@ -36,7 +50,8 @@
3650
extra_compile_args=cargs,
3751
swig_opts=['-keyword', '-nodefaultctor',
3852
'-nodefaultdtor', '-noproxy'],
39-
optional=True)]
53+
optional=True,
54+
**ext_limited_args)]
4055
else:
4156
mod = []
4257

@@ -61,4 +76,5 @@
6176
"Operating System :: OS Independent",
6277
"Intended Audience :: Science/Research",
6378
"Topic :: Scientific/Engineering",
64-
])
79+
],
80+
**setup_limited_args)

src/ihm_format.i

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ static struct category_handler_data *do_add_handler(
411411
const char *key_name;
412412
PyObject *o = PySequence_GetItem(keywords, i);
413413
if (PyUnicode_Check(o)) {
414-
key_name = PyUnicode_AsUTF8(o);
414+
key_name = PyUnicode_AsUTF8AndSize(o, NULL);
415415
if (PySet_Contains(int_keywords, o) == 1) {
416416
hd->keywords[i] = ihm_keyword_int_new(category, key_name);
417417
} else if (PySet_Contains(float_keywords, o) == 1) {

test/test_dumper.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1472,9 +1472,14 @@ def test_nonpoly_scheme_dumper(self):
14721472

14731473
def test_collection_dumper(self):
14741474
"""Test CollectionDumper"""
1475-
system = ihm.System()
1475+
system = ihm.System(id='1abc')
1476+
# Collection with no explicit entries (will use system ID)
14761477
c = ihm.Collection('foo', name='bar', details='more text')
14771478
system.collections.append(c)
1479+
# Collection with explicit entries
1480+
c = ihm.Collection('coll2', name='cname2', details='cdetails2',
1481+
entries=['2xyz', '3hjk'])
1482+
system.collections.append(c)
14781483
dumper = ihm.dumper._CollectionDumper()
14791484
out = _get_dumper_output(dumper, system)
14801485
self.assertEqual(out, """#
@@ -1483,6 +1488,15 @@ def test_collection_dumper(self):
14831488
_ihm_entry_collection.name
14841489
_ihm_entry_collection.details
14851490
foo bar 'more text'
1491+
coll2 cname2 cdetails2
1492+
#
1493+
#
1494+
loop_
1495+
_ihm_entry_collection_mapping.collection_id
1496+
_ihm_entry_collection_mapping.entry_id
1497+
foo 1abc
1498+
coll2 2xyz
1499+
coll2 3hjk
14861500
#
14871501
""")
14881502

test/test_format.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1263,14 +1263,29 @@ def test_cif_token_reader_replace_category_filter(self):
12631263
_cat5.bar
12641264
_cat5.baz
12651265
a b
1266+
#
1267+
_cat6.a 1
1268+
#
1269+
loop_
1270+
_cat7.bar
1271+
_cat7.baz
1272+
a b
1273+
#
12661274
"""
12671275
d = ihm.dumper._CommentDumper()
1276+
colld = ihm.dumper._CollectionDumper()
12681277
s = ihm.System()
12691278
s.comments.extend(['comment1', 'comment2'])
1279+
c = ihm.Collection('foo', name='bar', details='more text')
1280+
s.collections.append(c)
12701281
r = ihm.format.CifTokenReader(StringIO(cif))
12711282
filters = [ihm.format.ReplaceCategoryFilter("cat1"),
12721283
ihm.format.ReplaceCategoryFilter("_cat2", raw_cif='FOO'),
12731284
ihm.format.ReplaceCategoryFilter("cat3", dumper=d,
1285+
system=s),
1286+
ihm.format.ReplaceCategoryFilter("cat6", dumper=colld,
1287+
system=s),
1288+
ihm.format.ReplaceCategoryFilter("cat7", dumper=colld,
12741289
system=s)]
12751290
tokens = list(r.read_file(filters))
12761291
new_cif = "".join(x.as_mmcif() for x in tokens)
@@ -1289,6 +1304,32 @@ def test_cif_token_reader_replace_category_filter(self):
12891304
_cat5.bar
12901305
_cat5.baz
12911306
a b
1307+
#
1308+
loop_
1309+
_ihm_entry_collection.id
1310+
_ihm_entry_collection.name
1311+
_ihm_entry_collection.details
1312+
foo bar 'more text'
1313+
#
1314+
#
1315+
loop_
1316+
_ihm_entry_collection_mapping.collection_id
1317+
_ihm_entry_collection_mapping.entry_id
1318+
foo model
1319+
#
1320+
loop_
1321+
_ihm_entry_collection.id
1322+
_ihm_entry_collection.name
1323+
_ihm_entry_collection.details
1324+
foo bar 'more text'
1325+
#
1326+
#
1327+
loop_
1328+
_ihm_entry_collection_mapping.collection_id
1329+
_ihm_entry_collection_mapping.entry_id
1330+
foo model
1331+
1332+
#
12921333
""")
12931334

12941335
def test_category_token_group(self):

test/test_reader.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,13 +223,23 @@ def test_collection_handler(self):
223223
_ihm_entry_collection.name
224224
_ihm_entry_collection.details
225225
foo bar 'more text'
226+
c2id c2name c2details
227+
#
228+
_ihm_entry_collection_mapping.collection_id c2id
229+
_ihm_entry_collection_mapping.entry_id entry1
226230
"""
227231
for fh in cif_file_handles(cif):
228232
s, = ihm.reader.read(fh)
229-
c, = s.collections
230-
self.assertEqual(c.id, 'foo')
231-
self.assertEqual(c.name, 'bar')
232-
self.assertEqual(c.details, 'more text')
233+
c1, c2 = s.collections
234+
self.assertEqual(c1.id, 'foo')
235+
self.assertEqual(c1.name, 'bar')
236+
self.assertEqual(c1.details, 'more text')
237+
self.assertEqual(c1.entries, [])
238+
239+
self.assertEqual(c2.id, 'c2id')
240+
self.assertEqual(c2.name, 'c2name')
241+
self.assertEqual(c2.details, 'c2details')
242+
self.assertEqual(c2.entries, ['entry1'])
233243

234244
def test_software_handler(self):
235245
"""Test SoftwareHandler"""

0 commit comments

Comments
 (0)