Re: [Distutils] nspkg.pth files break $PYTHONPATH overrides

2016-12-20 Thread Elvis Stansvik
Hi all,

Sorry to bump this 2 year+ old thread.

This recently caused some serious head scratching. Barry's excellent
analysis of what happens seems spot on on to me.

Does anyone know if this issue was ever "resolved" or turned into a
bug report? I can see that the Debian packaging of the lazr.* packages
that Barry used as example still has this in their debian/rules:

override_dh_auto_install:
dh_auto_install
find debian/python*-lazr.* -name '*.pth' -delete

I.e. they undo the "accidental" installation of nspkg.pth files that
install_egg_info caused.

I'm doing the same in our packages right now, but it feels like a
band-aid solution.

In my case it's a bunch of company internal packages that all install
under an "orexplore" namespace package. I'm actually distributing with
a __init__.py for the namespace package and using namespace_packages=
in setup.py, so it's not PEP 420 from the get go. It only becomes a
PEP 420 when the debhelper has its way with it (stripping the
__init__.py, but problematically also installing the nspkg.pth).

Many thanks in advance.

Elvis

On Mar 24, 2014, at 5:48 PM, Barry Warsaw  wrote:
> Apologies for cross-posting, but this intersects setuptools and the import
> system, and I wanted to be sure it reached the right audience.
>
> A colleague asked me why a seemingly innocent and common use case for
> developing local versions of system installed packages wasn't working, and I
> was quite perplexed.  As I dug into the problem, more questions than answers
> came up.  I finally (think! I) figured out what is happening, but not so much
> as to why, or what can/should be done about it.
>
> This person had a local checkout of a package's source, where the package was
> also installed into the system Python.  He wanted to be able to set
> $PYTHONPATH so that the local package wins when he tries to import it.  E.g.:
>
> % PYTHONPATH=`pwd`/src python3
>
> but this didn't work because despite the setting of PYTHONPATH, the system
> version of the package was always found first.  The package in question is
> lazr.uri, although other packages with similar layouts will also suffer the
> same problem, which prevents an easy local development of a newer version of
> the package, aside from being a complete head-scratcher.
>
> The lazr.uri package is intended to be a submodule of the lazr namespace
> package.  As such, the lazr/__init__.py has the old style way of declaring a
> namespace package:
>
> try:
>import pkg_resources
>pkg_resources.declare_namespace(__name__)
> except ImportError:
>import pkgutil
>__path__ = pkgutil.extend_path(__path__, __name__)
>
> and its setup.py declares a namespace package:
>
> setup(
>name='lazr.uri',
>version=__version__,
>namespace_packages=['lazr'],
>...
>
> One of the things that the Debian "helper" program does when it builds a
> package for the archive is call `$python setup.py install_egg_info`.  It's
> this command that breaks $PYTHONPATH overriding.
>
> install_egg_info looks at the lazr.uri.egg-info/namespace_packages.txt file,
> in which it finds the string 'lazr', and it proceeds to write a
> lazr-uri-1.0.3-py3.4-nspkg.pth file.  This causes other strange and unexpected
> things to happen:
>
> % python3
> Python 3.4.0 (default, Mar 22 2014, 22:51:25)
> [GCC 4.8.2] on linux
> Type "help", "copyright", "credits" or "license" for more information.
 import sys
 sys.modules['lazr']
> 
 sys.modules['lazr'].__path__
> ['/usr/lib/python3/dist-packages/lazr']
>
> It's completely weird that sys.modules would contain a key for 'lazr' when
> that package was never explicitly imported.  Even stranger, because a fake
> module object is stuffed into sys.modules via the .pth file, tracing imports
> with -v gives you no clue as to what's happening.  And while
> sys.modules['lazr'] has an __path__, it has no other attributes.
>
> I really don't understand what the purpose of the nspkg.pth file is,
> especially for Python 3 namespace packages.
>
> Here's what the nspkg.pth file contains:
>
> import sys,types,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], 
> *('lazr',)); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie 
> and sys.modules.setdefault('lazr',types.ModuleType('lazr')); mp = (m or []) 
> and m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p)
>
> The __path__ value is important here because even though you've never
> explicitly imported 'lazr', when you *do* explicitly import 'lazr.uri', the
> existing lazr module object's __path__ takes over, and thus the system
> lazr.uri package is found even though both lazr/ and lazr/uri/ should have
> been found earlier on sys.path (yes, sys.path looks exactly as expected).
>
> So the presence of the nspkg.pth file breaks $PYTHONPATH overriding.  That
> seems bad. ;)
>
> If you delete the nspkg.path file, then things work as expected, but even this
> is a little misleading!
>
> I think the Debian helper is running instal

Re: [Distutils] nspkg.pth files break $PYTHONPATH overrides

2014-03-25 Thread PJ Eby
On Tue, Mar 25, 2014 at 3:50 PM, Barry Warsaw  wrote:

> On Mar 25, 2014, at 03:35 PM, PJ Eby wrote:
>
> >I think the correct fix would be to change the nspkg.pth magic to check
> for
> >PEP 420 support, but unfortunately it seems we may have to use version
> >checking: on rereading PEP 420 I see there's no 'sys.namespace_packages'
> or
> >similar object that can be directly checked for feature support.  :-(
>
> There is.  It's *pronounced* sys.namespace_packages, but it's spelled
> importlib._bootstrap._NamespaceLoader ;)
>

Yeah, well that's not exactly a public attribute, so it's not necessarily a
great way to do it.  But if we did use that, presumably it'd add something
like:

hnp =
hasattr(sys.modules.get('importlib._bootstrap',None),'_NamespaceLoader');

To the front of the magic, and then prefix all subsequent expression values
with 'not hnp and' in order to prevent them executing if PEP 420 support is
available. (Note: it's checking sys.modules since on any interpreter where
PEP 420 is natively available, the module should *already* be loaded by the
time site.py does its thing.)

And we should probably think about adding sys.namespace_packages or
something of the sort, or at least a proper flag for whether PEP 420
support is available on the platform.
___
Distutils-SIG maillist  -  Distutils-SIG@python.org
https://mail.python.org/mailman/listinfo/distutils-sig


Re: [Distutils] nspkg.pth files break $PYTHONPATH overrides

2014-03-25 Thread Barry Warsaw
On Mar 25, 2014, at 03:35 PM, PJ Eby wrote:

>I think the correct fix would be to change the nspkg.pth magic to check for
>PEP 420 support, but unfortunately it seems we may have to use version
>checking: on rereading PEP 420 I see there's no 'sys.namespace_packages' or
>similar object that can be directly checked for feature support.  :-(

There is.  It's *pronounced* sys.namespace_packages, but it's spelled
importlib._bootstrap._NamespaceLoader ;)

http://youtu.be/ehKGlT2EW1Q?t=44s

Cheers,
-Barry


signature.asc
Description: PGP signature
___
Distutils-SIG maillist  -  Distutils-SIG@python.org
https://mail.python.org/mailman/listinfo/distutils-sig


Re: [Distutils] nspkg.pth files break $PYTHONPATH overrides

2014-03-25 Thread PJ Eby
On Mon, Mar 24, 2014 at 6:09 PM, Barry Warsaw  wrote:

> On Mar 24, 2014, at 05:53 PM, Donald Stufft wrote:
>
> >See also https://github.com/pypa/pip/issues/3
>
> Hah, yeah.  I didn't realize --single-version-externally-managed is
> implied by
> --root, and in fact our Debian build scripts often (either explicitly or
> implicitly) define --root, as is the case in lazr.uri.
>
> >Basically prior to PEP420 namespace packages were bad and using them
> results
> >in pain sooner or later :( I'm not sure if a good solution yet, perhaps we
> >can backport PEP420 to PyPI and have namespace packages depend on that?
>
> Or maybe do a version check before installing this file?  Python 3.3 and
> newer
> will have PEP 420 support, and this file makes no sense in that case.
>

That won't *quite* work, because the .pth files are generated for other
types of direct-install distributions.

I think the correct fix would be to change the nspkg.pth magic to check for
PEP 420 support, but unfortunately it seems we may have to use version
checking: on rereading PEP 420 I see there's no 'sys.namespace_packages' or
similar object that can be directly checked for feature support.  :-(
___
Distutils-SIG maillist  -  Distutils-SIG@python.org
https://mail.python.org/mailman/listinfo/distutils-sig


Re: [Distutils] nspkg.pth files break $PYTHONPATH overrides

2014-03-25 Thread Brett Cannon
On Mon Mar 24 2014 at 6:09:48 PM, Barry Warsaw  wrote:

> On Mar 24, 2014, at 05:53 PM, Donald Stufft wrote:
>
> >See also https://github.com/pypa/pip/issues/3
>
> Hah, yeah.  I didn't realize --single-version-externally-managed is
> implied by
> --root, and in fact our Debian build scripts often (either explicitly or
> implicitly) define --root, as is the case in lazr.uri.
>
> >Basically prior to PEP420 namespace packages were bad and using them
> results
> >in pain sooner or later :( I’m not sure if a good solution yet, perhaps we
> >can backport PEP420 to PyPI and have namespace packages depend on that?
>
> Or maybe do a version check before installing this file?  Python 3.3 and
> newer
> will have PEP 420 support, and this file makes no sense in that case.
>
> (Backporting PEP 420 support to Python 3.2 might be useful, though a pain
> without importlib.  There's no sense in backporting to Python 3.1, IMHO.)
>

Importlib is in Python 3.2. You would just need to do the right things to
set up the environment and set __import__ to avoid doubling the stat calls
on a failed import.
___
Distutils-SIG maillist  -  Distutils-SIG@python.org
https://mail.python.org/mailman/listinfo/distutils-sig


Re: [Distutils] nspkg.pth files break $PYTHONPATH overrides

2014-03-24 Thread Barry Warsaw
On Mar 24, 2014, at 05:53 PM, Donald Stufft wrote:

>See also https://github.com/pypa/pip/issues/3

Hah, yeah.  I didn't realize --single-version-externally-managed is implied by
--root, and in fact our Debian build scripts often (either explicitly or
implicitly) define --root, as is the case in lazr.uri.

>Basically prior to PEP420 namespace packages were bad and using them results
>in pain sooner or later :( I’m not sure if a good solution yet, perhaps we
>can backport PEP420 to PyPI and have namespace packages depend on that?

Or maybe do a version check before installing this file?  Python 3.3 and newer
will have PEP 420 support, and this file makes no sense in that case.

(Backporting PEP 420 support to Python 3.2 might be useful, though a pain
without importlib.  There's no sense in backporting to Python 3.1, IMHO.)

-Barry


signature.asc
Description: PGP signature
___
Distutils-SIG maillist  -  Distutils-SIG@python.org
https://mail.python.org/mailman/listinfo/distutils-sig


Re: [Distutils] nspkg.pth files break $PYTHONPATH overrides

2014-03-24 Thread Donald Stufft

See also https://github.com/pypa/pip/issues/3

Basically prior to PEP420 namespace packages were bad and using them
results in pain sooner or later :( I’m not sure if a good solution yet, perhaps
we can backport PEP420 to PyPI and have namespace packages depend
on that?

On Mar 24, 2014, at 5:48 PM, Barry Warsaw  wrote:

> Apologies for cross-posting, but this intersects setuptools and the import
> system, and I wanted to be sure it reached the right audience.
> 
> A colleague asked me why a seemingly innocent and common use case for
> developing local versions of system installed packages wasn't working, and I
> was quite perplexed.  As I dug into the problem, more questions than answers
> came up.  I finally (think! I) figured out what is happening, but not so much
> as to why, or what can/should be done about it.
> 
> This person had a local checkout of a package's source, where the package was
> also installed into the system Python.  He wanted to be able to set
> $PYTHONPATH so that the local package wins when he tries to import it.  E.g.:
> 
> % PYTHONPATH=`pwd`/src python3
> 
> but this didn't work because despite the setting of PYTHONPATH, the system
> version of the package was always found first.  The package in question is
> lazr.uri, although other packages with similar layouts will also suffer the
> same problem, which prevents an easy local development of a newer version of
> the package, aside from being a complete head-scratcher.
> 
> The lazr.uri package is intended to be a submodule of the lazr namespace
> package.  As such, the lazr/__init__.py has the old style way of declaring a
> namespace package:
> 
> try:
>import pkg_resources
>pkg_resources.declare_namespace(__name__)
> except ImportError:
>import pkgutil
>__path__ = pkgutil.extend_path(__path__, __name__)
> 
> and its setup.py declares a namespace package:
> 
> setup(
>name='lazr.uri',
>version=__version__,
>namespace_packages=['lazr'],
>...
> 
> One of the things that the Debian "helper" program does when it builds a
> package for the archive is call `$python setup.py install_egg_info`.  It's
> this command that breaks $PYTHONPATH overriding.
> 
> install_egg_info looks at the lazr.uri.egg-info/namespace_packages.txt file,
> in which it finds the string 'lazr', and it proceeds to write a
> lazr-uri-1.0.3-py3.4-nspkg.pth file.  This causes other strange and unexpected
> things to happen:
> 
> % python3
> Python 3.4.0 (default, Mar 22 2014, 22:51:25) 
> [GCC 4.8.2] on linux
> Type "help", "copyright", "credits" or "license" for more information.
 import sys
 sys.modules['lazr']  
> 
 sys.modules['lazr'].__path__  
> ['/usr/lib/python3/dist-packages/lazr']
> 
> It's completely weird that sys.modules would contain a key for 'lazr' when
> that package was never explicitly imported.  Even stranger, because a fake
> module object is stuffed into sys.modules via the .pth file, tracing imports
> with -v gives you no clue as to what's happening.  And while
> sys.modules['lazr'] has an __path__, it has no other attributes.
> 
> I really don't understand what the purpose of the nspkg.pth file is,
> especially for Python 3 namespace packages.
> 
> Here's what the nspkg.pth file contains:
> 
> import sys,types,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], 
> *('lazr',)); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie 
> and sys.modules.setdefault('lazr',types.ModuleType('lazr')); mp = (m or []) 
> and m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p)
> 
> The __path__ value is important here because even though you've never
> explicitly imported 'lazr', when you *do* explicitly import 'lazr.uri', the
> existing lazr module object's __path__ takes over, and thus the system
> lazr.uri package is found even though both lazr/ and lazr/uri/ should have
> been found earlier on sys.path (yes, sys.path looks exactly as expected).
> 
> So the presence of the nspkg.pth file breaks $PYTHONPATH overriding.  That
> seems bad. ;)
> 
> If you delete the nspkg.path file, then things work as expected, but even this
> is a little misleading!
> 
> I think the Debian helper is running install_egg_info as a way to determine
> what namespace packages are defined, so that it can actually *remove* the
> parent's __init__.py file and use PEP 420 style namespace packages.  In fact,
> in the Debian python3-lazr.uri binary package, you find no system
> lazr/__init__.py file.  This is why removing the nspkg.pth file works.
> 
> So I thought, why not conditionally define setup(..., namespace_packages) only
> for Python 2?  This doesn't work because the Debian helper will see that no
> namespace packages are defined, and thus it will leave the original
> lazr/__init__.py file in place.  This then breaks $PYTHONPATH overriding too
> because of __path__ extension of the pre-PEP 420 code only *appends* the local
> development path.  IOW, the system import path is the first element of 

[Distutils] nspkg.pth files break $PYTHONPATH overrides

2014-03-24 Thread Barry Warsaw
Apologies for cross-posting, but this intersects setuptools and the import
system, and I wanted to be sure it reached the right audience.

A colleague asked me why a seemingly innocent and common use case for
developing local versions of system installed packages wasn't working, and I
was quite perplexed.  As I dug into the problem, more questions than answers
came up.  I finally (think! I) figured out what is happening, but not so much
as to why, or what can/should be done about it.

This person had a local checkout of a package's source, where the package was
also installed into the system Python.  He wanted to be able to set
$PYTHONPATH so that the local package wins when he tries to import it.  E.g.:

% PYTHONPATH=`pwd`/src python3

but this didn't work because despite the setting of PYTHONPATH, the system
version of the package was always found first.  The package in question is
lazr.uri, although other packages with similar layouts will also suffer the
same problem, which prevents an easy local development of a newer version of
the package, aside from being a complete head-scratcher.

The lazr.uri package is intended to be a submodule of the lazr namespace
package.  As such, the lazr/__init__.py has the old style way of declaring a
namespace package:

try:
import pkg_resources
pkg_resources.declare_namespace(__name__)
except ImportError:
import pkgutil
__path__ = pkgutil.extend_path(__path__, __name__)

and its setup.py declares a namespace package:

setup(
name='lazr.uri',
version=__version__,
namespace_packages=['lazr'],
...

One of the things that the Debian "helper" program does when it builds a
package for the archive is call `$python setup.py install_egg_info`.  It's
this command that breaks $PYTHONPATH overriding.

install_egg_info looks at the lazr.uri.egg-info/namespace_packages.txt file,
in which it finds the string 'lazr', and it proceeds to write a
lazr-uri-1.0.3-py3.4-nspkg.pth file.  This causes other strange and unexpected
things to happen:

% python3
Python 3.4.0 (default, Mar 22 2014, 22:51:25) 
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.modules['lazr']  

>>> sys.modules['lazr'].__path__  
['/usr/lib/python3/dist-packages/lazr']

It's completely weird that sys.modules would contain a key for 'lazr' when
that package was never explicitly imported.  Even stranger, because a fake
module object is stuffed into sys.modules via the .pth file, tracing imports
with -v gives you no clue as to what's happening.  And while
sys.modules['lazr'] has an __path__, it has no other attributes.

I really don't understand what the purpose of the nspkg.pth file is,
especially for Python 3 namespace packages.

Here's what the nspkg.pth file contains:

import sys,types,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], 
*('lazr',)); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie and 
sys.modules.setdefault('lazr',types.ModuleType('lazr')); mp = (m or []) and 
m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p)

The __path__ value is important here because even though you've never
explicitly imported 'lazr', when you *do* explicitly import 'lazr.uri', the
existing lazr module object's __path__ takes over, and thus the system
lazr.uri package is found even though both lazr/ and lazr/uri/ should have
been found earlier on sys.path (yes, sys.path looks exactly as expected).

So the presence of the nspkg.pth file breaks $PYTHONPATH overriding.  That
seems bad. ;)

If you delete the nspkg.path file, then things work as expected, but even this
is a little misleading!

I think the Debian helper is running install_egg_info as a way to determine
what namespace packages are defined, so that it can actually *remove* the
parent's __init__.py file and use PEP 420 style namespace packages.  In fact,
in the Debian python3-lazr.uri binary package, you find no system
lazr/__init__.py file.  This is why removing the nspkg.pth file works.

So I thought, why not conditionally define setup(..., namespace_packages) only
for Python 2?  This doesn't work because the Debian helper will see that no
namespace packages are defined, and thus it will leave the original
lazr/__init__.py file in place.  This then breaks $PYTHONPATH overriding too
because of __path__ extension of the pre-PEP 420 code only *appends* the local
development path.  IOW, the system import path is the first element of a
2-element list on lazr.__path__.  While the local import path is the second
element, in this case too the local import fails.

It seems like what you want for Python 3 (and we're talking >= 3.2 here) is
for there to be neither a nspkg.pth file, nor the lazr/__init__.py file, and
let PEP 420 do it's thing.  In fact if you set things up this way, $PYTHONPATH
overriding works exactly as expected.

Because I don't know why install_egg_info is installing the nspkg.pth file, I
don't know which component needs