From f9cdfc0578f45aff007565aead06f4b5f756cd7b Mon Sep 17 00:00:00 2001 From: Brett Viren Date: Tue, 19 Nov 2024 08:44:15 -0500 Subject: [PATCH] Use importlib instead of janky use of eval. Apparently needed for Python 3.13 in any case. Fix #24. --- python/gegede/builder.py | 8 +++----- python/gegede/configuration.py | 12 ++++++------ python/gegede/export/__init__.py | 7 ++++--- python/gegede/util.py | 17 +++++++++++++++++ 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/python/gegede/builder.py b/python/gegede/builder.py index 4c234e6..22aaff7 100644 --- a/python/gegede/builder.py +++ b/python/gegede/builder.py @@ -5,7 +5,7 @@ from collections import OrderedDict -from .util import list_match +from .util import list_match, make_class class Builder(object): ''' @@ -88,10 +88,8 @@ def add_builder(self, name, klass): print ('Builder already registered: "%s"' % name) return - if (isinstance(klass, type(""))): - mod,name = klass.rsplit('.',1) # this seems dirty - exec ('import %s' % mod) - klass = eval(klass) + if isinstance(klass, str): + klass = make_class(klass) builder = klass(name) self.builders[name] = builder return diff --git a/python/gegede/configuration.py b/python/gegede/configuration.py index e55ff02..829d7b9 100644 --- a/python/gegede/configuration.py +++ b/python/gegede/configuration.py @@ -5,6 +5,7 @@ import os import re from collections import OrderedDict +from .util import make_class def parse(filenames): '''Parse configuration files. @@ -40,6 +41,7 @@ def cfg2pod(cfg): pod[secname] = secdat return pod + interp_reobj = re.compile(r'{([\w:]+)}') def interpolate_value(value, secname, sections): def getter(match): @@ -53,6 +55,7 @@ def getter(match): ret = re.subn(interp_reobj, getter, value) return ret[0] + def interpolate(sections): '''Perform string interpolation values . @@ -74,15 +77,12 @@ def interpolate(sections): if changed: continue break - - -def make_class(fqclass): - mod, cls = fqclass.rsplit('.',1) - exec('import %s' % mod) # better way? - return eval(fqclass) def make_value(v, **kwds): + ''' + Return value of string. + ''' from . import Quantity return eval(v, globals(), dict(Q=Quantity, **kwds)) diff --git a/python/gegede/export/__init__.py b/python/gegede/export/__init__.py index 398b12c..65e15e1 100644 --- a/python/gegede/export/__init__.py +++ b/python/gegede/export/__init__.py @@ -6,17 +6,18 @@ ''' +from gegede.util import make_module + class Exporter(object): ''' Wrapper around an exporter module which helps to enforce the conventions. ''' def __init__(self, mod): - if isinstance(mod, type("")): + if isinstance(mod, str): if not '.' in mod: mod = 'gegede.export.' + mod print ('Importing: "%s"' % mod) - exec('import %s' % mod) - mod = eval(mod) + mod = make_module(mod) self.mod = mod self.obj = None diff --git a/python/gegede/util.py b/python/gegede/util.py index f30c188..7f8d5cc 100644 --- a/python/gegede/util.py +++ b/python/gegede/util.py @@ -2,6 +2,7 @@ ''' ''' +from importlib import import_module from gegede import Quantity def wash_units(obj, lunit='cm', aunit='radian'): @@ -71,3 +72,19 @@ def list_match(values, entry = None, deref = lambda x: x): ret.append(v) return list(ret) + +def make_module(mod): + ''' + Return a module object given pkg.mod string. + ''' + return import_module(mod) + + +def make_class(fqclass): + ''' + Return a class given pkg.mod.Class string. + ''' + mod, cls = fqclass.rsplit('.',1) + mod = import_module(mod) + return getattr(mod, cls) +