instance as module

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

instance as module

Robin Becker
I'm trying to overcome a recursive import issue in reportlab.

Module reportlab.rl_config uses various sources (eg ~/.reportlab_settings) to
initialize various defaults eg canvas_basefontname. If a user wants to utilize
reportlab to set up such a default, it's not possible to do so in the settings
file because a recursive import would occur. Normal defaults are always
primitive python objects eg float/int/str etc etc.

If I make the rl_config module into an object (using the GvR blessed approach
indicated here http://stackoverflow.com/questions/2447353/getattr-on-a-module)
then I can use callables in the settings module and get them evaluated lazily
which overcomes the recursion.

Are there any gotcha's that need to be considered when using this instance as
module approach? My trial implementation seems to work, but it's not clear
exactly how a module differs from an instance. I have set various dunders on the
instance eg __file__, __doc__, __all__ & __name__ and I made the object a borg,
but it still seems a bit hacky.
--
Robin Becker


Reply | Threaded
Open this post in threaded view
|

instance as module

Peter Otten
Robin Becker wrote:

> I'm trying to overcome a recursive import issue in reportlab.
>
> Module reportlab.rl_config uses various sources (eg ~/.reportlab_settings)
> to initialize various defaults eg canvas_basefontname. If a user wants to
> utilize reportlab to set up such a default, it's not possible to do so in
> the settings file because a recursive import would occur. Normal defaults
> are always primitive python objects eg float/int/str etc etc.
>
> If I make the rl_config module into an object (using the GvR blessed
> approach indicated here
> http://stackoverflow.com/questions/2447353/getattr-on-a-module) then I can
> use callables in the settings module and get them evaluated lazily which
> overcomes the recursion.
>
> Are there any gotcha's that need to be considered when using this instance
> as module approach? My trial implementation seems to work, but it's not
> clear exactly how a module differs from an instance. I have set various
> dunders on the instance eg __file__, __doc__, __all__ & __name__ and I
> made the object a borg, but it still seems a bit hacky.

Do I understand this correctly? You got bitten by a complex setup and now
you are hoping to improve the situation by making it even more complex?

How about reordering initialisation in such a way that the user defaults are
the last thing being set?


Reply | Threaded
Open this post in threaded view
|

instance as module

Robin Becker
On 19/06/2015 11:23, Peter Otten wrote:
> Robin Becker wrote:
.........
>
> Do I understand this correctly? You got bitten by a complex setup and now
> you are hoping to improve the situation by making it even more complex?
>
> How about reordering initialisation in such a way that the user defaults are
> the last thing being set?
>

I don't think re-ordering is feasible or desirable.

For the specific case of the canvas_basefontname I don't actually need to do
anything specific since it is just a string, however, before it can be used in
action support has to be provided ie I must register the font used. I could just
make the few usages of this value check for callability at use time, but that
would scatter the problem; if one default is special why not all.

Effectively the defaults setup is not complex enough to allow use of itself;
that must be a fairly common problem.

Probably the correct thing to do was always to use an object to hold the
defaults. For a long time a module satisfied, then people wanted long running
processes, reset etc etc now they want to define things ahead of time (probably
for good reason).

I could just move the rl_config module into reportlab's __init__ as an object,
but I'm almost sure that would break someone's

    from reportlab.rl_config import *

which although deprecated is still allowed. Defining a module like object seems
a reasonable way out if I really need it.
--
Robin Becker


Reply | Threaded
Open this post in threaded view
|

instance as module

Ben Finney-10
Robin Becker <robin at reportlab.com> writes:

> For the specific case of the canvas_basefontname I don't actually need
> to do anything specific since it is just a string

The configuration values should be nothing but immutable values:
strings, integers, booleans. Possibly collections of those.

You seem to be implying that some configuration values are complex
objects; that's undesirable.

You seem to be further implying that configuration is executable code;
that is *definitely* undesirable (look at the awful mess created by
making Distutils configuration an executable program).

> however, before it can be used in action support has to be provided ie
> I must register the font used.

That's fine, it is a step that should come *after* configuration is
entirely completed and the static values are known.

> I could just make the few usages of this value check for callability
> at use time, but that would scatter the problem; if one default is
> special why not all.

Indeed. The correct solution is not to make configuration executable,
but instead make it static data, read by the program as simple immutable
values.

> Effectively the defaults setup is not complex enough to allow use of
> itself; that must be a fairly common problem.

The common solution is:

* Use a non-executable configuration file format. ?configparser? from
  the standard library is a good option, YAML is another.

* Use the conventional precedence order for applying configuration
  values: default values, overridden by config files, overridden by
  environment variables, overridden by command-line options.

* Only after configuration using the above precedence is complete, use
  the immutable values defined to change program behaviour (e.g. create
  or modify dynamic, complex objects that respond to the config
  options).

If you've got a config file that is executable, and attempts to define
program objects, then yes you're going to get the complex mess you
describe in your initial posting of this thread.

Avoid that by migrating away from such a poor configuration format:
Switch to a ?configparser? format, or some other static, non-executable
data file.

> Probably the correct thing to do was always to use an object to hold
> the defaults.

Yes, a ?configparser.ConfigParser()? instance can store defaults which
will be over-ridden by the sequence of configuration files read.

--
 \           ?I do not believe in forgiveness as it is preached by the |
  `\        church. We do not need the forgiveness of God, but of each |
_o__)                    other and of ourselves.? ?Robert G. Ingersoll |
Ben Finney


Reply | Threaded
Open this post in threaded view
|

instance as module

Dieter Maurer
In reply to this post by Robin Becker
Robin Becker <robin at reportlab.com> writes:

> I'm trying to overcome a recursive import issue in reportlab.
> ... sketched solution ...

In the "zope" project, the same problem was approached in a slightly different
way -- see the product "zope.deferredimport". It allows to defer
an actual import for an imported name until this name is accessed.

Looking a bit deeper, I recognize that behind the scenes,
"zope.deferredimport" uses a technique similar to yours:
it replaces the original module by a proxy - which almost behaves
like the original module but has the additional capacity to do special things
for accessed to names not yet in the modules dictionary.

"zope.deferredproxy" is widely used in "zope" projects and I have not
seen any related problem report. Looks as its implementation technique
is rather safe.


You might be able to use "zope.deferredimport" directly for your purposes
or look at its implementation to learn what things the implementors
have taken care of. Or you might just learn from it, that replacing
a module by something similar is quite safe.

Dieter