Package sourceinfo ::
Module helper
1
2 """pysourceinfo.helper - miscellaneous utilities - details see manuals
3
4 The provided interfaces are foreseen for the bottom layer of the software stack,
5 thus provide basic handling only. This includes special cases of the file system
6 parameters. For more advanced file system path processing refer to *filesysobjects*.
7 """
8 from __future__ import absolute_import
9 from __future__ import print_function
10
11 import os
12 import sys
13 import re
14 from inspect import stack
15 from imp import PY_SOURCE, PY_COMPILED, C_EXTENSION, PKG_DIRECTORY, C_BUILTIN, PY_FROZEN
16
17 from pythonids import PYV35Plus, ISSTR
18
19 from sourceinfo import SourceInfoError, \
20 presolve, P_FIRST, P_LAST, P_LONGEST, P_SHORTEST, P_IGNORE0, \
21 OID_STR, OID_TUPLE, OID_LIST
22
23 if PYV35Plus:
24
25 from importlib.machinery import SOURCE_SUFFIXES, DEBUG_BYTECODE_SUFFIXES, OPTIMIZED_BYTECODE_SUFFIXES, BYTECODE_SUFFIXES, EXTENSION_SUFFIXES
26 from importlib import util
27
28 else:
29 from inspect import getmoduleinfo
30
31
32 __author__ = 'Arno-Can Uestuensoez'
33 __license__ = "Artistic-License-2.0 + Forced-Fairplay-Constraints"
34 __copyright__ = "Copyright (C) 2010-2017 Arno-Can Uestuensoez" \
35 " @Ingenieurbuero Arno-Can Uestuensoez"
36 __version__ = '0.1.21'
37 __uuid__ = '9de52399-7752-4633-9fdc-66c87a9200b8'
38
39 __docformat__ = "restructuredtext en"
40
41
42
43 __MT_UNKNOWN = 0
44 __MT_SOURCE = 1
45 __MT_COMPILED = 2
46 __MT_EXTENSION = 3
47 __MT_DIRECTORY = 5
48 __MT_BUILTIN = 6
49 __MT_FROZEN = 7
50 __MT_COMPILED_OPT1 = 10
51 __MT_COMPILED_OPT2 = 18
52 __MT_COMPILED_DEBUG = 34
53
54
55 _scname = re.compile(
56 r'''.*[\\\\/]([^.]+)[.]([^-]+)-([0-9]{2})[.](opt-[012])*([.].+)$''')
57
58
59 CUT_INIT_PY = re.compile(r'__init__.py')
60
138
139
141 """File path name for OID.
142 """
143 find_spec = util.find_spec(oid, spath)
144 if not find_spec is None:
145 return find_spec.origin
146
147
149 """Matches prefix from sys.path.
150 Foreseen to be used for canonical base references in unit tests.
151 This enables in particular for generic tests of file system positions
152 where originally absolute pathnames were required.
153
154 Args:
155 pname:
156 Path name to be searched an appropriate
157 pythonpath prefix for, either absolute
158 or relative to an item of *plist*.
159
160 plist:
161 List of possible python paths.
162 Alternative to *sys.path*.
163
164 default := sys.path
165
166 kw:
167 ispre:
168 If *True* adds a postfix path separator.
169
170 presolve:
171 Defines the path resolution
172 strategy::
173
174 presolve := (
175 P_FIRST
176 | P_LAST
177 | P_LONGEST
178 | P_SHORTEST
179 )
180
181 The default is stored in::
182
183 sourceinfo.presolve = P_FIRST
184
185 verify:
186 Verify against the filesystem, else no checks
187 are done.
188
189 default := True
190
191 Returns:
192 Returns the first matching path prefix from sys.path,
193 as 'normpath'.
194 Else::
195
196 ispre == True: returns empty str - ''
197 else: returns None
198
199 Raises:
200 SourceInfoError
201
202 pass-through
203
204 """
205 _pre = kw.get('ispre')
206 _verify = kw.get('verify', True)
207 if not pname:
208
209 if _verify:
210 raise SourceInfoError('no valid param pname = "%s"' % (str(pname)))
211 if _pre:
212 return ''
213 return None
214 if not plist:
215 plist = sys.path
216
217 _presolve = kw.get('presolve', presolve)
218
219
220 try:
221 _fp = os.path.normpath(pname)
222 except TypeError:
223 try:
224 _fp = os.path.normpath(pname.__file__)
225 except:
226 if _verify:
227 raise SourceInfoError('not valid pname = %s' % (str(pname)))
228
229
230
231
232
233 if os.path.isabs(_fp):
234 if pname in plist:
235
236
237 if _verify and not os.path.exists(pname):
238
239 raise SourceInfoError('does not exist: \n abs: %s' % (str(pname)))
240
241
242 if _pre:
243 return _fp + os.sep
244 return _fp
245
246 if _verify:
247 _fplst = []
248 for x in plist:
249 if _fp.startswith(x) and os.path.exists(x):
250 _fplst.append(x)
251
252 else:
253 _fplst = [x for x in plist if _fp.startswith(x)]
254
255 else:
256 _fplst = []
257 for _sp in plist:
258 if _verify and not os.path.exists(_sp + os.sep + _fp):
259 continue
260
261 if _pre:
262 _fplst.append(_sp + os.sep)
263 else:
264 _fplst.append(_sp)
265
266 if _verify and not _fplst:
267 raise SourceInfoError('does not exist: %s' % (str(pname)))
268
269 if _presolve == P_FIRST:
270 if _verify and not os.path.exists(_fplst[0]):
271 raise SourceInfoError('does not exist: %s' % (str(_fplst[0])))
272 return _fplst[0]
273
274 elif _presolve == P_LAST:
275 if _verify and not os.path.exists(_fplst[-1]):
276 raise SourceInfoError('does not exist: %s' % (str(_fplst[-1])))
277 return _fplst[-1]
278
279 elif _presolve == P_LONGEST:
280 _fpnew = ''
281 for x in _fplst:
282 if _verify and not os.path.exists(x):
283 continue
284
285 if len(x.split(os.sep)) > len(_fpnew.split(os.sep)):
286 _fpnew = x
287
288 elif _presolve == P_SHORTEST:
289 _fpnew = ''
290 for x in _fplst:
291 if _verify and not os.path.exists(x):
292 continue
293
294 if not _fpnew:
295 _fpnew = x
296
297 elif len(x.split(os.sep)) < len(_fpnew.split(os.sep)):
298 _fpnew = x
299
300 else:
301 raise SourceInfoError("unknown presolve = " + str(presolve))
302
303
304 if _verify and not _fpnew:
305 raise SourceInfoError('does not exist: %s' % (str(pname)))
306
307 return _fpnew
308
309
311 """Verifies an absolute or relative path name to be
312 reachable as a relative path to one item of the search
313 list *plist*.
314
315 Args:
316 pname:
317 Path name of a path searched for relative to
318 an item of *plist*. Absolute is cut, relative
319 is searched literally.
320
321 plist:
322 Search path alternative to *sys.path*.
323
324 *default := sys.path*
325
326 kargs:
327 presolve:
328 The type of path resolution: ::
329
330 pname is relative:
331
332 presolve := (
333 P_FIRST
334 )
335
336 pname is absolute:
337
338 presolve := (
339 P_FIRST
340 | P_LAST
341 | P_LONGEST
342 | P_SHORTEST
343 )
344
345 *default := pysourceinfo.presolve(P_FIRST)*
346
347 raw:
348 If *True* suppresses the call of 'os.path.normpath()'::
349
350 raw := (
351 True # keep e.g. multiple and trailing *os.sep*
352 | False # apply os.path.normpath()
353 )
354 default := False
355
356 normpath:
357 The method to be called for the normalization of the provided
358 input path. This is in particular required when cross-platform
359 path strings are provided. These are by default processed
360 internally with the *os.sep* of the current platform only.
361
362 The *API* is as defined by 'os.path.normpath'::
363
364 normapth := (
365 <custom-call>
366 | <default-call>
367 )
368 custom-call := (
369 ntpath
370 | posixpath
371 | <any-compatible-api>
372 )
373 default-call := os.path.normpath
374
375 verify:
376 Verify against the file system, else no checks
377 are done.
378
379 *default := True*
380
381 Returns:
382 First matched path from *sys.path* as normalized path.
383 Else::
384
385 if PYTHONPATH[i] == pname: returns empty str - ''
386 else:
387 if verify: raise SourceInfoError
388 else: returns None
389
390 Raises:
391 SourceInfoError
392
393 pass-through
394
395 """
396 _normpath = kw.get('normpath', os.path.normpath)
397 _raw = kw.get('raw', False)
398 _verify = kw.get('verify')
399 if not pname:
400
401 if _verify:
402 raise SourceInfoError('no valid param pname = "%s"' % (str(pname)))
403 return pname
404 if not plist:
405 plist = sys.path
406
407 if os.path.isabs(pname):
408 _presolve = kw.get('presolve', presolve)
409 else:
410 _presolve = P_FIRST
411
412
413
414 if not _raw and _normpath:
415 try:
416 _fp = _normpath(pname)
417 except TypeError:
418 try:
419 _fp = _normpath(pname.__file__)
420 except:
421 if _verify:
422 raise SourceInfoError('not valid pname = %s' % (str(pname)))
423 else:
424 _fp = pname
425
426
427
428
429
430 if os.path.isabs(_fp):
431 if pname in plist:
432
433
434 if _verify and not os.path.exists(pname):
435
436 raise SourceInfoError('does not exist: \n abs: %s' % (str(pname)))
437 if pname == _fp:
438 return ''
439 return _fp
440
441
442 _fplst = [x for x in plist if x and _fp.startswith(x)]
443
444 else:
445 _fplst = []
446 for _sp in plist:
447 if os.path.exists(_sp + os.sep + _fp):
448 _fplst.append(_sp)
449 else:
450 continue
451
452
453 if not _fplst:
454 if pname == '':
455 if not _verify:
456 return ''
457 if not _verify:
458 return
459 raise SourceInfoError('does not exist: %s' % (str(pname)))
460
461 if _presolve == P_FIRST:
462 if _verify and not os.path.exists(_fplst[0]):
463 raise SourceInfoError('does not exist: %s' % (str(_fplst[0])))
464 if os.path.isabs(_fp):
465 _ret = _fp[len(_fplst[0]):]
466 if os.path.isabs(_ret):
467 return _ret[1:]
468 else:
469 return _fp
470
471 elif _presolve == P_LAST:
472 if _verify and not os.path.exists(_fplst[-1]):
473 raise SourceInfoError('does not exist: %s' % (str(_fplst[-1])))
474 if os.path.isabs(_fp):
475 _ret = _fp[len(_fplst[-1]):]
476 if os.path.isabs(_ret):
477 return _ret[1:]
478 else:
479 return _fp
480
481 elif _presolve == P_LONGEST:
482 _fpnew = ''
483
484 _shortest = ''
485 for x in _fplst:
486 if _verify and not os.path.exists(x):
487 continue
488
489 if not _shortest:
490 _shortest = x
491
492 for y in plist:
493 if x.startswith(y):
494 if y and len(y.split(os.sep)) < len(_shortest.split(os.sep)):
495 _shortest = y
496
497 if not _fpnew:
498 _fpnew = x
499
500 elif len(x.split(os.sep)) < len(_fpnew.split(os.sep)):
501
502 _fpnew = x
503
504
505 if _fpnew:
506 if _fpnew[-1] == os.sep:
507 return pname[len(_fpnew):]
508 return pname[len(_fpnew) + 1:]
509
510 elif _presolve == P_SHORTEST:
511
512 kw['presolve'] = P_LONGEST
513 _longest = getpythonpath(_fp, plist, **kw)
514 _sp = _fp[len(_longest):]
515
516 if os.path.isabs(_sp):
517 return _sp[1:]
518 return _sp
519
520 else:
521 raise SourceInfoError("unknown presolve = " + str(presolve))
522
523 if _verify and not _fpnew:
524 raise SourceInfoError('does not exist: %s' % (str(pname)))
525
526 return _fpnew
527
528
530 """Calls *getpythonpath_rel*, returns the relative
531 path as dotted *OID* relative to the search path.
532
533 Args:
534 For interface parameters refer to::
535
536 sourceinfo.helper.getpythonpath_rel()
537
538 kargs:
539 For remaining parameters refer to
540 *getpythonpath_rel*.
541
542 restype:
543 Result type::
544
545 restype = (
546 OID_STR # OID as dottedt string representation
547 | OID_TUPLE # OID as tuple
548 | OID_LIST # OID as list
549 )
550
551 default := OID_STR
552
553 sepin:
554 The path separator::
555
556 default := os.sep
557
558 The function internally checks the presence of the provided path.
559 This is not cross-converted, thus requires native separator
560 characters. While the *NT* file system supports multiple separators,
561 the *posix* based file systems support regularly the slash '/' only.
562 Thus these fail when other separator characters are provided. For
563 advanced cross-capabilities refer to *filesysobjects*.
564
565 sepout:
566 The path separator::
567
568 default := '.'
569
570 Returns:
571 Returns the provided result by *getpythonpath_rel* in accordance to
572 the requested result type - see *restype*.
573
574 Raises:
575 SourceInfoError
576
577 pass-through
578
579 """
580 _restype = kw.get('restype', OID_STR)
581 _sepin = kw.get('sepin', os.sep)
582 _sepout = kw.get('sepout', '.')
583
584 if not isinstance(pname, ISSTR):
585 raise SourceInfoError("Supports string input only, got:" + str(pname))
586
587
588 _res = getpythonpath_rel(CUT_INIT_PY.sub('', pname), plist=None, **kw)
589 if not _res:
590 return
591
592 if isinstance(_res, ISSTR):
593 if _restype == OID_STR:
594 return re.sub(re.escape(_sepin), _sepout, _res)
595 elif _restype == OID_TUPLE:
596 return tuple(re.split(re.escape(_sepin), _res))
597 elif _restype == OID_LIST:
598 return list(re.split(re.escape(_sepin), _res))
599
600 raise SourceInfoError("Supports string conversion only, got:" + str(_res))
601
602
604 """List of current mem-addresses on stack frames.
605
606 Args:
607 spos:
608 Stack position.
609
610 fromtop:
611 Start on top, default from bottom.
612
613 Returns:
614 Returns the specified frame.
615
616 Raises:
617 passed through exceptions
618
619 """
620 if fromtop:
621 return stack()[-1 * spos]
622 return stack()[spos]
623
624
626 """Length of current stack.
627
628 Args:
629 none.
630
631 Returns:
632 Returns the length of current stack.
633
634 Raises:
635 pass-through
636
637 """
638 return len(stack())
639
641 """Match a file pathname of pathname on a pathlist.
642
643 Args:
644 path:
645 Path to be searched in *pathlist*.
646
647 pathlist:
648 Search path for a given *path*.
649
650 default := *sys.path*
651
652 pmatch:
653 Match criteria for search. ::
654
655 pmatch := (
656 P_FIRST, P_LAST, P_SHORTEST,
657 P_LONGEST, P_IGNORE0
658 )
659
660 default := *P_FIRST*
661
662 Returns:
663 Returns the matched pathname from pathlist.
664
665 Raises:
666 pass-through
667
668 """
669 _buf = None
670 if pathlist == None:
671 pathlist = sys.path
672
673 if pmatch & P_IGNORE0:
674 _pl = pathlist[1:]
675 else:
676 _pl = pathlist
677 for sx in _pl:
678 p0 = os.path.normpath(sx)
679 if path.startswith(p0):
680 if pmatch & P_FIRST:
681 return p0
682 elif pmatch & P_LAST:
683 _buf = p0
684 elif pmatch & P_LONGEST:
685 if len(p0) > len(_buf):
686 _buf = p0
687 elif pmatch & P_SHORTEST:
688 if len(p0) < len(_buf) or not _buf:
689 _buf = p0
690 return _buf
691