usfmtc 0.2.2__tar.gz → 0.2.3__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.
Files changed (32) hide show
  1. {usfmtc-0.2.2/python/lib/usfmtc.egg-info → usfmtc-0.2.3}/PKG-INFO +1 -1
  2. {usfmtc-0.2.2 → usfmtc-0.2.3}/pyproject.toml +1 -1
  3. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc/__init__.py +15 -13
  4. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc/extension.py +2 -2
  5. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc/usfmparser.py +49 -39
  6. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc/xmlutils.py +13 -3
  7. {usfmtc-0.2.2 → usfmtc-0.2.3/python/lib/usfmtc.egg-info}/PKG-INFO +1 -1
  8. {usfmtc-0.2.2 → usfmtc-0.2.3}/LICENSE.md +0 -0
  9. {usfmtc-0.2.2 → usfmtc-0.2.3}/README.md +0 -0
  10. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/README.md +0 -0
  11. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc/diagrams.py +0 -0
  12. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc/grammar.py +0 -0
  13. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc/parser.py +0 -0
  14. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc/railroad.py +0 -0
  15. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc/reference.py +0 -0
  16. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc/rnc2rng/__init__.py +0 -0
  17. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc/rnc2rng/parser.py +0 -0
  18. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc/rnc2rng/rnctree.py +0 -0
  19. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc/rnc2rng/serializer.py +0 -0
  20. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc/sfmparser.py +0 -0
  21. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc/usfmgenerate.py +0 -0
  22. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc/usjproc.py +0 -0
  23. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc/usx.rng +0 -0
  24. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc/usxgrammar.py +0 -0
  25. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc/usxmodel.py +0 -0
  26. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc/usxparser.py +0 -0
  27. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc.egg-info/SOURCES.txt +0 -0
  28. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc.egg-info/dependency_links.txt +0 -0
  29. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc.egg-info/entry_points.txt +0 -0
  30. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc.egg-info/requires.txt +0 -0
  31. {usfmtc-0.2.2 → usfmtc-0.2.3}/python/lib/usfmtc.egg-info/top_level.txt +0 -0
  32. {usfmtc-0.2.2 → usfmtc-0.2.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: usfmtc
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: USFM reference parser
5
5
  Author-email: USFM Technical Committee <fonts@sil.org>
6
6
  License: Copyright © 2022-2023 by the members of the USFM/USX technical committee, listed below:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "usfmtc"
7
- version = "0.2.2"
7
+ version = "0.2.3"
8
8
  authors = [{name = "USFM Technical Committee", email = "fonts@sil.org"}]
9
9
  readme = "python/README.md"
10
10
  license = {file = "LICENSE.md"}
@@ -58,15 +58,15 @@ def _usfmGrammar(rdoc, backend=None, start=None):
58
58
 
59
59
  def usfmGrammar(gsrc, extensions=[], altparser=True, backend=None, start=None):
60
60
  """ Create UsfmGrammarParser from gsrc as used by USX.fromUsfm """
61
- if len(extensions) or not altparser:
62
- rdoc = _grammarDoc(gsrc, extensions)
63
61
  if altparser:
62
+ rdoc = _grammarDoc(gsrc, extensions)
63
+ return _usfmGrammar(rdoc, backend, start)
64
+ else:
64
65
  res = Grammar()
65
66
  if len(extensions):
66
67
  for e in extensions:
67
68
  res.readmrkrs(e)
68
69
  return res
69
- return _usfmGrammar(rdoc, backend, start)
70
70
 
71
71
 
72
72
  _filetypes = {".xml": "usx", ".usx": "usx", ".usfm": "usfm", ".sfm": "usfm3.0", ".json": "usj"}
@@ -129,10 +129,12 @@ class USX:
129
129
  @classmethod
130
130
  def fromUsfm(cls, src, grammar, altparser=True, elfactory=None, timeout=1e7):
131
131
  """ Parses USFM using UsfmGrammarParser grammar and creates USX object.
132
- Raise usfmtc.parser.NoParseError on error. """
132
+ Raise usfmtc.parser.NoParseError on error.
133
+ elfactory must take parent and pos named parameters not as attributes
134
+ """
133
135
  data = _readsrc(src)
134
136
 
135
- if altparser:
137
+ if not altparser:
136
138
  p = USFMParser(data, factory=elfactory or ParentElement, grammar=grammar)
137
139
  xml = p.parse()
138
140
  else:
@@ -181,7 +183,7 @@ class USX:
181
183
  def outUsfm(self, grammar, file=None, altparser=True, **kw):
182
184
  """ Output USFM from USX object. grammar is et doc. If file is None returns string """
183
185
  el = messup(self.xml)
184
- if altparser:
186
+ if not altparser:
185
187
  return self._outwrite(file, el, fn=usx2usfm)
186
188
  parser = USXConverter(grammar.getroot(), **kw)
187
189
  res = parser.parse(el)
@@ -217,7 +219,7 @@ class USX:
217
219
  elif outtype == "usj":
218
220
  self.outUsj(outfpath)
219
221
  elif outtype == "usfm":
220
- if grammar is None and not altparser:
222
+ if grammar is None and altparser:
221
223
  if gramfile is None:
222
224
  for a in ([], ['..', '..', '..', 'grammar']):
223
225
  gramfile = os.path.join(os.path.dirname(__file__), *a, "usx.rng")
@@ -263,7 +265,7 @@ def main(hookcli=None, hookusx=None):
263
265
  parser.add_argument("-e","--esids",action="store_true",help="Add esids, vids, sids, etc. to USX output")
264
266
  parser.add_argument("-v","--version",default=None,help="Set USFM version [3.1]")
265
267
  parser.add_argument("-x","--extfiles",action="append",default=[],help="markers.ext files to include")
266
- parser.add_argument("--qusfm",action="store_true",help="Use quick nonvalidating USFM parser")
268
+ parser.add_argument("-V","--validate",action="store_true",help="Use validating parsers for USFM")
267
269
  parser.add_argument("-C","--canonical",action="store_true",help="Do not canonicalise")
268
270
  parser.add_argument("-l","--logging",help="Set logging level to usfmxtest.log")
269
271
  parser.add_argument("-q","--quiet",action="store_true",help="Don't say much")
@@ -326,16 +328,16 @@ def main(hookcli=None, hookusx=None):
326
328
  if args.informat.startswith("usfm"):
327
329
  args.extfiles.append(os.path.join(os.path.dirname(infiles[0]), "markers.ext"))
328
330
  exts = [x for x in args.extfiles if os.path.exists(x)]
329
- ingrammar = usfmGrammar(args.grammar, altparser=args.qusfm, extensions=exts)
331
+ ingrammar = usfmGrammar(args.grammar, altparser=args.validate, extensions=exts)
330
332
  if args.outformat and args.outformat.startswith("usfm"):
331
333
  outgrammar = _grammarDoc(args.grammar)
332
334
 
333
335
  for infile in infiles:
334
336
  outfile = None
335
337
  if args.outfile is None:
336
- outfile = _makeoutfile(infile, args.format)
338
+ outfile = _makeoutfile(infile, args.outformat)
337
339
  elif os.path.isdir(args.outfile):
338
- outf = _makeoutfile(infile, args.format)
340
+ outf = _makeoutfile(infile, args.outformat)
339
341
  if outf is None:
340
342
  doerror(f"invalid output format {args.format} in {args.outfile}")
341
343
  outfile = os.path.join(args.outfile, os.path.basename(outf))
@@ -351,7 +353,7 @@ def main(hookcli=None, hookusx=None):
351
353
  if not args.quiet:
352
354
  print(f"{infile} -> {outfile}" if outfile else f"{infile}")
353
355
  try:
354
- usxdoc = readFile(infile, informat=args.informat, grammar=ingrammar, altparser=args.qusfm)
356
+ usxdoc = readFile(infile, informat=args.informat, grammar=ingrammar, altparser=args.validate)
355
357
  except NoParseError as e:
356
358
  doerror(f"Failed to parse {infile}: {e}", False)
357
359
 
@@ -373,7 +375,7 @@ def main(hookcli=None, hookusx=None):
373
375
  if not args.canonical:
374
376
  usxdoc.canonicalise()
375
377
 
376
- usxdoc.saveAs(outfile, outformat=args.outformat, addesids=args.esids, grammar=outgrammar, altparser=args.qusfm)
378
+ usxdoc.saveAs(outfile, outformat=args.outformat, addesids=args.esids, grammar=outgrammar, altparser=args.validate)
377
379
 
378
380
  if __name__ == "__main__":
379
381
  main()
@@ -58,11 +58,11 @@ class Extensions(SFMFile):
58
58
 
59
59
  def applyto(self, rdoc, factory=et):
60
60
  res = False # nothing added
61
- allcats = set([x.get('category', None) for x in self.markers.values()])
61
+ allcats = set([x.get('category', "").split()[0] for x in self.markers.values()])
62
62
  for a in allcats:
63
63
  if a not in categories:
64
64
  continue
65
- ms = [k for k, v in self.markers.items() if v.get('category', None) == a]
65
+ ms = [k for k, v in self.markers.items() if a in v.get('category', "").split()]
66
66
  enum = categories[a]+".style.enum"
67
67
  e = rdoc.find('./{0}define[@name="{1}"]/{0}choice'.format(relaxns, enum))
68
68
  allmks = set([v.text for v in e.findall(f'./{relaxns}value')])
@@ -5,8 +5,14 @@ import xml.etree.ElementTree as et
5
5
  from usfmtc.extension import SFMFile
6
6
  from collections import UserDict, UserString
7
7
 
8
+
9
+ class Pos:
10
+ def __init__(self, l, c):
11
+ self.l = l
12
+ self.c = c
13
+
8
14
  class Tag(str):
9
- def __new__(cls, s):
15
+ def __new__(cls, s, l=0, c=0):
10
16
  isend = False
11
17
  isplus = False
12
18
  if s.startswith("+"):
@@ -23,7 +29,7 @@ class Tag(str):
23
29
  def __init__(self, s, l=0, c=0):
24
30
  self.isplus = getattr(self, 'isplus', False)
25
31
  self.isend = getattr(self, 'isend', False)
26
- self.pos = (l, c)
32
+ self.pos = Pos(l, c)
27
33
 
28
34
  def __repr__(self):
29
35
  return "Tag("+str(self)+")"
@@ -43,12 +49,12 @@ class Tag(str):
43
49
  class AttribText(UserString):
44
50
  def __init__(self, s, l=0, c=0):
45
51
  super().__init__(s)
46
- self.pos = (l, c)
52
+ self.pos = Pos(l, c)
47
53
  pass
48
54
 
49
55
  class OptBreak:
50
56
  def __init__(self, l=0, c=0):
51
- self.pos = (l, c)
57
+ self.pos = Pos(l, c)
52
58
 
53
59
  def __repr__(self):
54
60
  return "OptBreak(" + str(self) + ")"
@@ -58,7 +64,7 @@ class OptBreak:
58
64
  class String(UserString):
59
65
  def __init__(self, s, l=0, c=0):
60
66
  super().__init__(s)
61
- self.pos = (l, c)
67
+ self.pos = Pos(l, c)
62
68
 
63
69
  def __add__(self, s):
64
70
  return String(str(self) + s)
@@ -68,7 +74,7 @@ class String(UserString):
68
74
  class Attribs(UserDict):
69
75
  def __init__(self, l=0, c=0):
70
76
  super().__init__()
71
- self.pos = (l, c)
77
+ self.pos = Pos(l, c)
72
78
 
73
79
  class Lexer:
74
80
 
@@ -113,7 +119,7 @@ class Lexer:
113
119
  curri = m.end() + 1
114
120
  continue
115
121
  else:
116
- res = Tag(t.group(0))
122
+ res = Tag(t.group(0), l=self.lindex, c=curri-self.lpos)
117
123
  curri += t.end()
118
124
  break
119
125
  elif n == '|':
@@ -179,7 +185,7 @@ class Grammar:
179
185
  "versepara": "cls nb pc pi1 pi2 pi3 pi po pr pmo pmc pmr pm ph1 ph2 ph3 ph p q1 q2 q3 q4 qc qr qm1 qm2 qm3 qm qd q b d mi1 mi2 mi3 mi4 mi m",
180
186
  }
181
187
 
182
- marker_categories = {t:k for k, v in category_markers.items() for t in v.split(' ')}
188
+ marker_categories = {t:k for k, v in category_markers.items() for t in v.split()}
183
189
 
184
190
  attribmap = { 'jmp' : 'href', 'k' : 'key', 'qt-s': 'who', 'qt1-s': 'who', 'qt2-s': 'who',
185
191
  'qt3-s': 'who', 'qt4-s': 'who', 'qt5-s': 'who', 'rb': 'gloss', 't-s': 'sid', 'ts-s': 'sid',
@@ -211,13 +217,13 @@ def isfirstText(e):
211
217
  return True
212
218
 
213
219
  class Node:
214
- def __init__(self, parser, usxtag, tag, ispara=False, notag=False):
220
+ def __init__(self, parser, usxtag, tag, ispara=False, notag=False, pos=None):
215
221
  self.parser = parser
216
222
  self.tag = tag
217
223
  self.ispara = ispara
218
224
  parent = parser.stack[-1] if len(parser.stack) else None
219
225
  self.element = parser.factory(usxtag, {} if notag or tag is None else {"style": tag},
220
- parent=getattr(parent, 'element', None))
226
+ parent=getattr(parent, 'element', None), pos=pos)
221
227
  if parent:
222
228
  parent.addNodeElement(self.element)
223
229
  self.attribnodes = []
@@ -227,7 +233,7 @@ class Node:
227
233
 
228
234
  def appendElement(self, child):
229
235
  if isinstance(child, OptBreak):
230
- c = self.parser.factory("optbreak", {}, parent=self.element)
236
+ c = self.parser.factory("optbreak", {}, parent=self.element, pos=child.pos)
231
237
  self.element.append(c)
232
238
  self.clearAttribNodes()
233
239
 
@@ -297,11 +303,12 @@ class USXNode(Node):
297
303
  self.element.set('version', str(t).strip())
298
304
 
299
305
  class AttribNode(Node):
300
- def __init__(self, parser, parent, tag):
306
+ def __init__(self, parser, parent, tag, pos=None):
301
307
  self.parser = parser
302
308
  self.parent = parent
303
309
  self.parent.addAttribNode(self)
304
310
  self.tag = tag
311
+ self.pos = pos
305
312
  self.attribnodes = []
306
313
 
307
314
  def addNodeElement(self, e):
@@ -313,8 +320,8 @@ class AttribNode(Node):
313
320
  self.parent.element.set(attrib, str(t).strip())
314
321
 
315
322
  class NumberNode(Node):
316
- def __init__(self, parser, usxtag, tag, ispara=False):
317
- super().__init__(parser, usxtag, tag, ispara=ispara)
323
+ def __init__(self, parser, usxtag, tag, ispara=False, pos=None):
324
+ super().__init__(parser, usxtag, tag, ispara=ispara, pos=pos)
318
325
  self.hasarg = False
319
326
 
320
327
  def appendText(self, t):
@@ -327,7 +334,7 @@ class NumberNode(Node):
327
334
  if len(b) > 1 and b[1].strip():
328
335
  self.parser.removeTag(self.tag)
329
336
  if self.ispara:
330
- self.parser.addNode(Node(self.parser, 'para', 'p'))
337
+ self.parser.addNode(Node(self.parser, 'para', 'p', pos=self.element.pos))
331
338
  self.parser.stack[-1].appendText(b[1])
332
339
 
333
340
  def addNodeElement(self, e):
@@ -335,8 +342,8 @@ class NumberNode(Node):
335
342
  self.parser.stack[-1].addNodeElement(e)
336
343
 
337
344
  class NoteNode(Node):
338
- def __init__(self, parser, usxtag, tag):
339
- super().__init__(parser, usxtag, tag)
345
+ def __init__(self, parser, usxtag, tag, pos=None):
346
+ super().__init__(parser, usxtag, tag, pos=pos)
340
347
  self.hascaller = False
341
348
 
342
349
  def appendText(self, t):
@@ -384,7 +391,10 @@ class USFMParser:
384
391
 
385
392
  def __init__(self, txt, factory=None, grammar=None):
386
393
  if factory is None:
387
- factory = et.Element
394
+ def makeel(tag, attrib, **extras):
395
+ attrib.update({" "+k:v for k, v in extras.items()})
396
+ return et.Element(tag, attrib)
397
+ factory = makeel
388
398
  if grammar is None:
389
399
  grammar = Grammar()
390
400
  self.factory = factory
@@ -467,31 +477,31 @@ class USFMParser:
467
477
  if tag.isend:
468
478
  return self.removeTag(str(tag))
469
479
  elif self.stack[-1].tag == "c":
470
- parent = AttribNode(self, self.stack[-1], str(tag))
480
+ parent = AttribNode(self, self.stack[-1], str(tag), pos=tag.pos)
471
481
  else:
472
- parent = Node(self, 'para', str(tag), ispara=True)
482
+ parent = Node(self, 'para', str(tag), ispara=True, pos=tag.pos)
473
483
  self.stack.append(parent)
474
484
  return parent
475
485
 
476
486
  def _esb(self, tag):
477
487
  self.removeType(paratypes, paratags)
478
- return self.addNode(Node(self, "sidebar", str(tag)))
488
+ return self.addNode(Node(self, "sidebar", str(tag), pos=tag.pos))
479
489
 
480
490
  def _esbe(self, tag):
481
491
  return self.removeTag("esb")
482
492
 
483
493
  def _periph(self, tag):
484
494
  self.removeTag(str(tag))
485
- return self.addNode(PeriphNode(self, "periph", str(tag), notag=True))
495
+ return self.addNode(PeriphNode(self, "periph", str(tag), notag=True, pos=tag.pos))
486
496
 
487
497
  def _ref(self, tag):
488
498
  if tag.isend:
489
499
  return self.removeTag(str(tag))
490
- return self.addNode(Node(self, 'ref', tag.basestr(), notag=True))
500
+ return self.addNode(Node(self, 'ref', tag.basestr(), notag=True, pos=tag.pos))
491
501
 
492
502
  def _rem(self, tag):
493
503
  self.removeType(paratypes, paratags)
494
- res = self.addNode(Node(self, 'para', str(tag), ispara=True))
504
+ res = self.addNode(Node(self, 'para', str(tag), ispara=True, pos=tag.pos))
495
505
  self.lexer.readLine()
496
506
  return res
497
507
 
@@ -499,23 +509,23 @@ class USFMParser:
499
509
  self.removeTag('tr')
500
510
  if not len(self.stack) or self.stack[-1].element.tag != "table":
501
511
  self.removeType(paratypes,)
502
- self.addNode(Node(self, 'table', ' table', notag=True))
503
- return self.addNode(Node(self, 'row', 'tr'))
512
+ self.addNode(Node(self, 'table', ' table', notag=True, pos=tag.pos))
513
+ return self.addNode(Node(self, 'row', 'tr', pos=tag.pos))
504
514
 
505
515
  def _v(self, tag):
506
516
  self.removeTag('v')
507
517
  if len(self.stack) and self.stack[-1].tag == "c":
508
518
  self.removeTag('c')
509
- self.addNode(Node(self, 'para', 'p'))
510
- return self.addNode(NumberNode(self, "verse", str(tag)))
519
+ self.addNode(Node(self, 'para', 'p', pos=tag.pos))
520
+ return self.addNode(NumberNode(self, "verse", str(tag), pos=tag.pos))
511
521
 
512
522
  def _vp(self, tag):
513
523
  if tag.isend:
514
524
  return self.removeTag(str(tag))
515
525
  elif self.stack[-1].tag == "v":
516
- parent = AttribNode(self, self.stack[-1], str(tag))
526
+ parent = AttribNode(self, self.stack[-1], str(tag), pos=tag.pos)
517
527
  else:
518
- parent = Node(self, 'para', tag.basestr())
528
+ parent = Node(self, 'para', tag.basestr(), pos=tag.pos)
519
529
  self.stack.append(parent)
520
530
  return parent
521
531
 
@@ -523,43 +533,43 @@ class USFMParser:
523
533
  if self.grammar.marker_categories.get(self.stack[-1].tag, "") == "crossreferencechar":
524
534
  res = self.removeType('crossreferencechar')
525
535
  if not tag.isend:
526
- res = self.addNode(Node(self, 'char', tag.basestr()))
536
+ res = self.addNode(Node(self, 'char', tag.basestr(), pos=tag.pos))
527
537
  return res
528
538
 
529
539
  def attrib(self, tag):
530
540
  if tag.isend:
531
541
  return self.removeTag(str(tag))
532
542
  else:
533
- parent = AttribNode(self, self.stack[-1], str(tag))
543
+ parent = AttribNode(self, self.stack[-1], str(tag), pos=tag.pos)
534
544
  self.stack.append(parent)
535
545
  return parent
536
546
 
537
547
  def cell(self, tag):
538
548
  self.removeType('cell')
539
549
  if not tag.isend:
540
- return self.addNode(Node(self, 'cell', str(tag)))
550
+ return self.addNode(Node(self, 'cell', str(tag), pos=tag.pos))
541
551
  return self.parent
542
552
 
543
553
  def crossreference(self, tag):
544
554
  if tag.isend:
545
555
  return self.removeTag(str(tag))
546
- return self.addNode(NoteNode(self, 'note', str(tag)))
556
+ return self.addNode(NoteNode(self, 'note', str(tag), pos=tag.pos))
547
557
 
548
558
  def crossreferencechar(self, tag):
549
559
  res = self.removeType('crossreferencechar')
550
560
  if not tag.isend:
551
- res = self.addNode(Node(self, 'char', tag.basestr()))
561
+ res = self.addNode(Node(self, 'char', tag.basestr(), pos=tag.pos))
552
562
  return res
553
563
 
554
564
  def footnote(self, tag):
555
565
  if tag.isend:
556
566
  return self.removeTag(str(tag))
557
- return self.addNode(NoteNode(self, 'note', str(tag)))
567
+ return self.addNode(NoteNode(self, 'note', str(tag), pos=tag.pos))
558
568
 
559
569
  def footnotechar(self, tag):
560
570
  res = self.removeType('footnotechar')
561
571
  if not tag.isend:
562
- res = self.addNode(Node(self, 'char', tag.basestr()))
572
+ res = self.addNode(Node(self, 'char', tag.basestr(), pos=tag.pos))
563
573
  return res
564
574
 
565
575
  def ident(self, tag):
@@ -570,10 +580,10 @@ class USFMParser:
570
580
  def milestone(self, tag):
571
581
  if tag.isend:
572
582
  return self.removeType('milestone')
573
- return self.addNode(Node(self, 'ms', tag.basestr()))
583
+ return self.addNode(Node(self, 'ms', tag.basestr(), pos=tag.pos))
574
584
 
575
585
  def unknown(self, tag):
576
- self.addNode(Node(self, 'ms', str(tag)))
586
+ self.addNode(Node(self, 'ms', str(tag), pos=tag.pos))
577
587
  res = self.removeTag(tag)
578
588
  return res
579
589
 
@@ -3,12 +3,13 @@ import re
3
3
  import xml.etree.ElementTree as et
4
4
 
5
5
  class ParentElement(et.Element):
6
- def __init__(self, tag, attrib=None, parent=None):
6
+ def __init__(self, tag, attrib=None, parent=None, pos=None):
7
7
  et.Element.__init__(self, tag, attrib)
8
8
  self.parent = parent
9
+ self.pos = pos
9
10
 
10
- def makeelement(self, tag, attrib):
11
- return self.__class__(tag, attrib, parent=self)
11
+ def makeelement(self, tag, attrib, pos=None):
12
+ return self.__class__(tag, attrib, parent=self, pos=pos or self.pos)
12
13
 
13
14
  def __str__(self):
14
15
  return "{}[{}]".format(self.tag, " ".join('@{}="{}"'.format(k, v) for k, v in self.attrib.items()))
@@ -18,19 +19,24 @@ class ParentElement(et.Element):
18
19
  return "{}/{}".format(p, str(self))
19
20
 
20
21
  def _getindex(self):
22
+ ''' Finds my index in my parent, returning both '''
21
23
  if self.parent is None:
22
24
  return -1, None
23
25
  return list(self.parent).index(self), self.parent
24
26
 
25
27
  def getprevious(self):
28
+ ''' Returns the previous element with the same parent, if any '''
26
29
  i, parent = self._getindex()
27
30
  return parent[i-1] if parent is not None and i > 0 else None
28
31
 
29
32
  def getnext(self):
33
+ ''' Returns the next element with the same parent, if any '''
30
34
  i, parent = self._getindex()
31
35
  return parent[i+1] if parent is not None and i < len(parent) - 1 else None
32
36
 
33
37
  def getnext_sibling(self):
38
+ ''' Returns the next element, while going up and down the hierarchy.
39
+ Notice that any text at the start of an element stops any hierarchy descent '''
34
40
  i, parent = self._getindex()
35
41
  if i < len(parent) - 1:
36
42
  return parent[i+1]
@@ -41,6 +47,8 @@ class ParentElement(et.Element):
41
47
  return parent
42
48
 
43
49
  def getprevious_sibling(self):
50
+ ''' Returns the previous element, while going up and down the hierarchy
51
+ Notice that any text after an element stops any hierarchy descent '''
44
52
  i, parent = self._getindex()
45
53
  if i > 0:
46
54
  return parent[i-1]
@@ -51,11 +59,13 @@ class ParentElement(et.Element):
51
59
  return parent
52
60
 
53
61
  def addprevious(self, el):
62
+ ''' Inserts el before the current element in the same parent '''
54
63
  i, parent = self._getindex()
55
64
  if parent is not None:
56
65
  parent.insert(i, el)
57
66
 
58
67
  def getparent(self):
68
+ ''' Returns the parent '''
59
69
  return self.parent
60
70
 
61
71
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: usfmtc
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: USFM reference parser
5
5
  Author-email: USFM Technical Committee <fonts@sil.org>
6
6
  License: Copyright © 2022-2023 by the members of the USFM/USX technical committee, listed below:
File without changes
File without changes
File without changes
File without changes
File without changes