Skip to content

Conversation

@josephsl
Copy link
Contributor

@josephsl josephsl commented Sep 9, 2018

Link to issue number:

Fixes #8712
Fixes #8724
Fixes #8768

Summary of the issue:

More work on Python 3 imports, including relative imports and changing builtin to builtins, as well as using importlib to import modules at runtime.

Description of how this pull request fixes the issue:

Changes made:

  • Import builtin -> builtins (Python Console, language handler).
  • GUI and IAccessible objects: more relative imports (from . import ...).
  • Use importlib to import modules at runtime, including for IAccessible modules.

Testing performed:

Tested with Python 2.7 interpreter to make sure they are imported.

Known issues with pull request:

None

Change log entry:

None

* IAccessible: from winWord import ... -> from .winWord import ...
* GUI: from addonGui import ... -> from .addonGui import ...
Python Console and language handler imports __builtin__, which has been renamed to builtins in Python 3.
…ccessible support modules from the same folder. Re nvaccess#8712.

Python 3 wants a dot (.) when importing modules from the same folder. When using __import__ function, the level argument specifies search level (default is 0). Thus allow other IAccessible modules to be loaded by specifying level of 1 (relative import from the same folder).
@josephsl josephsl requested a review from feerrenrut September 9, 2018 05:20
@josephsl josephsl added the z Python 3 transition (archived) Python 3 transition label Sep 9, 2018
classString=classString[1:]
mod=__import__(modString,globals(),locals(),[])
# #8712: Python 3 wants a dot (.) when loading a module from the same folder via relative imports, and this is done via level argument.
mod=__import__(modString,globals(),locals(),[],1)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you call this with kwargs? Like:

mod=__import__(modString,globals=globals(),locals=locals(),level=1)

There is also the advice to use importlib.import_module, at least on python 2.

@josephsl
Copy link
Contributor Author

josephsl commented Sep 14, 2018 via email

@LeonarddeR
Copy link
Collaborator

@josephsl commented on 14 Sep 2018, 16:25 CEST:

Hi, importlib is new in Python 3 (if I read this correctly).

It is also there in Python 2.7.

Help on built-in function import in module builtin:

import(...)
import(name, globals={}, locals={}, fromlist=[], level=-1) -> module

Import a module. Because this function is meant for use by the Python

interpreter and not for general use, it is better to use
importlib.import_module() to programmatically import a module.

@josephsl
Copy link
Contributor Author

josephsl commented Sep 14, 2018 via email

@LeonarddeR
Copy link
Collaborator

I'd rather see this as part of this one. It creates unnecessary overhead if we have to change one line of code in this pr, and then change it again in the next.

See also #8768.

@josephsl josephsl changed the title Python 3 imports: additional work on builtins, relative imports Python 3 imports: additional work on builtins, relative imports, importlib Sep 20, 2018
…ly at runtime. Re nvaccess#8768.

Python 2.7 includes a base implementation of importlib which powers __import__ function, with Python 3 providing more features such as importers. Thus use importlib.import_module in app module handler and others, specifying appropriate package names in the process.
nvaccess#8712.

Because importlib.import_module complains about module names, spell the module name (NVDAObjects.IAccessible.mod).
@josephsl
Copy link
Contributor Author

Hi,

By the way, there is an unused "import Queue" in synthDrivers.espeak (no underscore), so I'm ignoring that one. People who will be working on transition should note that this import causes an exception, so should be removed then.

Thanks.

HRESULT no longer exists in ctypes.wintypes in Python 3. The new location is ctypes.HRESULT. Even in Python 2, ctypes.wintypes.HRESULT points to ctypes.HRESULT.
# Try querying the app module for the name of the app being hosted.
try:
# Python 2.x can't properly handle unicode module names, so convert them.
mod = __import__("appModules.%s" % appName.encode("mbcs"),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely going to fail on python 3

>>> "hello, %s" % "test".encode("mbcs")
"hello, b'test'"

str.format gives the same result.

It should be sufficient to check if not isinstance(appName, str) before encoding to mbcs.

@josephsl
Copy link
Contributor Author

josephsl commented Sep 24, 2018 via email

@LeonarddeR
Copy link
Collaborator

LeonarddeR commented Sep 24, 2018 via email

@josephsl
Copy link
Contributor Author

josephsl commented Sep 24, 2018 via email

…thon 2, no longer needed in Python 3.

Addressing review comment (reviewed by Leonard de Ruijter/Babbage): appName.encode('mbcs') will fail in Python 3, so do not do it unless one is using Python 2.
Note that this is an exception to the PR stream: nowhere in import_module call does encoding takes place.
@josephsl
Copy link
Contributor Author

Hi,

Addressed the import concern as of today's commit. As noted in the commit message, I'm making an exception, as nobody else calling import_module calls name.encode apart from app module handler.

Thanks.

# Try querying the app module for the name of the app being hosted.
# Python 2.x can't properly handle unicode module names, so convert them.
# No longer the case in Python 3.
if sys.version.startswith("2"):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO its more elegant to do if isinstance(appName, bytes): bytes is a shortcut to str on python 2 and a separate type in python 3, so it will automatically apply to python 2 in this case. Alternatively, you can use sys.version_info.major

@josephsl
Copy link
Contributor Author

josephsl commented Sep 25, 2018 via email

@LeonarddeR
Copy link
Collaborator

Never mind, I was horribly wrong there. This place actually encodes (goes from unicode from bytes), my head was telling me that we had to decode (from bytes to unicode). So if it would have been isinstance, the right clause would be if not isinstance(appname, str):

@josephsl
Copy link
Contributor Author

josephsl commented Dec 7, 2018

Hi,

@feerrenrut, may I request a second look for this one? I'm interested in seeing if conflicts may arise when attempting to merge this pull request in the future.

Thanks.

Copy link
Contributor

@feerrenrut feerrenrut left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me. Given that it is tested against python 2.7, I think we should merge it in now. We don't have any intention of continuing to support Python 2.7 once we move over to 3 so I will add a task to the python 3 project board to remove any 2.7 specific code.

@LeonarddeR
Copy link
Collaborator

I agree.

@josephsl
Copy link
Contributor Author

Possible regression reported: multi-byte file name issue. Multiple possibilities, including Python 2.7.16, this PR, or something else.

Thanks.

josephsl added a commit that referenced this pull request May 31, 2019
josephsl added a commit that referenced this pull request May 31, 2019
josephsl added a commit that referenced this pull request May 31, 2019
michaelDCurran pushed a commit that referenced this pull request Jun 1, 2019
@LeonarddeR
Copy link
Collaborator

@josephsl: Could you please file this again against threshold?

@josephsl
Copy link
Contributor Author

josephsl commented Jun 4, 2019 via email

@josephsl josephsl deleted the py3imports2 branch July 19, 2020 22:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

4 participants