Mercurial > cgi-bin > hgweb.cgi > tincan
comparison tincan.py @ 15:560c8fb55e4a draft
Fix bugs in #python directive, make code-behind class loading simpler.
author | David Barts <n5jrn@me.com> |
---|---|
date | Thu, 16 May 2019 18:42:22 -0700 |
parents | 9d0497dc19f8 |
children | 448fc3d534f8 |
comparison
equal
deleted
inserted
replaced
14:9d0497dc19f8 | 15:560c8fb55e4a |
---|---|
382 # If this is a #hidden page, we ignore it for now, since hidden pages | 382 # If this is a #hidden page, we ignore it for now, since hidden pages |
383 # don't get routes made for them. | 383 # don't get routes made for them. |
384 if oheader.hidden and not oheader.errors: | 384 if oheader.hidden and not oheader.errors: |
385 return | 385 return |
386 # Get the code-behind #python | 386 # Get the code-behind #python |
387 if self._header.python is not None: | 387 if self._header.python is None: |
388 self._python_specified = False | |
389 else: | |
388 if not self._header.python.endswith(_PEXTEN): | 390 if not self._header.python.endswith(_PEXTEN): |
389 raise TinCanError("{0}: #python files must end in {1}".format(self._urlpath, _PEXTEN)) | 391 raise TinCanError("{0}: #python files must end in {1}".format(self._urlpath, _PEXTEN)) |
390 self._python = self._header.python | 392 self._python = self._header.python |
393 self._python_specified = True | |
391 # Obtain a class object by importing and introspecting a module. | 394 # Obtain a class object by importing and introspecting a module. |
392 self._getclass() | 395 self._getclass() |
393 # Build body object (#template) | 396 # Build body object (#template) |
394 if self._header.template is not None: | 397 if self._header.template is not None: |
395 if not self._header.template.endswith(_TEXTEN): | 398 if not self._header.template.endswith(_TEXTEN): |
438 return 0 | 441 return 0 |
439 except OSError as e: | 442 except OSError as e: |
440 raise TinCanError(str(e)) from e | 443 raise TinCanError(str(e)) from e |
441 | 444 |
442 def _getclass(self): | 445 def _getclass(self): |
443 pypath = os.path.normpath(os.path.join(self._fsroot, *self._splitpath(self._python))) | 446 try: |
447 pypath = os.path.normpath(os.path.join(self._fsroot, *self._splitpath(self._python))) | |
448 except IndexError as e: | |
449 raise TinCanError("{0}: invalid #python".format(self._urlpath)) from e | |
444 klass = ErrorPage if self._header.errors else Page | 450 klass = ErrorPage if self._header.errors else Page |
445 # Give 'em a default code-behind if they don't furnish one | 451 # Give 'em a default code-behind if they don't furnish one |
446 pytime = self._gettime(pypath) | 452 pytime = self._gettime(pypath) |
447 if not pytime: | 453 if not pytime: |
454 if self._python_specified: | |
455 raise TinCanError("{0}: #python file not found".format(self._urlpath)) | |
448 self._class = klass | 456 self._class = klass |
449 return | 457 return |
450 # Else load the code-behind from a .py file | 458 # Else load the code-behind from a .py file |
451 pycpath = pypath + 'c' | 459 pycpath = pypath + 'c' |
452 pyctime = self._gettime(pycpath) | 460 pyctime = self._gettime(pycpath) |
461 spec = importlib.util.spec_from_file_location(_mangle(self._name), pycpath) | 469 spec = importlib.util.spec_from_file_location(_mangle(self._name), pycpath) |
462 mod = importlib.util.module_from_spec(spec) | 470 mod = importlib.util.module_from_spec(spec) |
463 spec.loader.exec_module(mod) | 471 spec.loader.exec_module(mod) |
464 except Exception as e: | 472 except Exception as e: |
465 raise TinCanError("{0}: error importing".format(pycpath)) from e | 473 raise TinCanError("{0}: error importing".format(pycpath)) from e |
474 # Locate a suitable class | |
466 self._class = None | 475 self._class = None |
467 for i in dir(mod): | 476 for i in dir(mod): |
468 v = getattr(mod, i) | 477 v = getattr(mod, i) |
469 if isclass(v) and issubclass(v, klass): | 478 if isclass(v) and issubclass(v, klass) and v is not klass: |
470 if self._class is None: | 479 if self._class is not None: |
471 self._class = v | 480 raise TinCanError("{0}: contains multiple {1} classes".format(pypath, klass.__name__)) |
472 else: | 481 self._class = v |
473 if self._class is klass: | |
474 self._class = v | |
475 elif v is not klass: | |
476 raise TinCanError("{0}: contains multiple {1} classes".format( | |
477 pypath, klass.__name__)) | |
478 if self._class is None: | 482 if self._class is None: |
479 raise TinCanError("{0}: contains no {1} classes".format(pypath, klass.__name__)) | 483 raise TinCanError("{0}: contains no {1} classes".format(pypath, klass.__name__)) |
480 | 484 |
481 def _redirect(self): | 485 def _redirect(self): |
482 try: | 486 try: |