dave-msk / merak

Python binary package builder (via Cython)
Apache License 2.0
32 stars 6 forks source link

rope AttributeError #16

Open rtommy opened 2 months ago

rtommy commented 2 months ago

I tried to build a binary extension and I got the following error:

  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "...\Scripts\merak.exe\__main__.py", line 7, in <module>
  File "...\Lib\site-packages\merak\main.py", line 34, in main
    app.run()
  File "...\Lib\site-packages\cement\core\foundation.py", line 948, in run
    return_val = self.controller._dispatch()
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\cement\ext\ext_argparse.py", line 810, in _dispatch
    return func()
           ^^^^^^
  File "...\Lib\site-packages\merak\commands\cythonize.py", line 57, in __call__
    success = cybuild.build_package_cython_extension(
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\merak\core\cybuild.py", line 74, in build_package_cython_extension
    mods, sub_pkgs = _restructure_package(tmp_proot, sep=sep)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\merak\core\cybuild.py", line 165, in _restructure_package
    project = rope_project.Project(pkg_root)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\rope\base\project.py", line 221, in __init__
    self._init_prefs(prefs)
  File "...\Lib\site-packages\rope\base\project.py", line 278, in _init_prefs
    self._init_ropefolder()
  File "...\Lib\site-packages\rope\base\project.py", line 250, in _init_ropefolder
    config.write(self._default_config())
  File "...\Lib\site-packages\rope\base\resources.py", line 136, in write
    self._perform_change(
  File "...\Lib\site-packages\rope\base\resources.py", line 101, in _perform_change
    self.project.do(changes)
  File "...\Lib\site-packages\rope\base\project.py", line 112, in do
    self.history.do(changes, task_handle=task_handle)
  File "...\Lib\site-packages\rope\base\history.py", line 37, in do
    changes.do(change.create_job_set(task_handle, changes))
  File "...\Lib\site-packages\rope\base\change.py", line 65, in do
    change.do(job_set)
  File "...\Lib\site-packages\rope\base\change.py", line 126, in call
    function(self)
  File "...\Lib\site-packages\rope\base\change.py", line 151, in do
    self._operations.write_file(self.resource, self.new_contents)
  File "...\Lib\site-packages\rope\base\change.py", line 340, in write_file
    observer.resource_changed(resource)
  File "...\Lib\site-packages\rope\base\resourceobserver.py", line 31, in resource_changed
    self.changed(resource)
  File "...\Lib\site-packages\rope\base\pycore.py", line 64, in _file_changed_for_soa
    perform_soa_on_changed_scopes(self.project, resource, old_contents)
  File "...\Lib\site-packages\rope\base\pycore.py", line 308, in perform_soa_on_changed_scopes
    pycore.analyze_module(resource, should_analyze, search_subscopes)
  File "...\Lib\site-packages\rope\base\pycore.py", line 216, in analyze_module
    rope.base.oi.soa.analyze_module(
  File "...\Lib\site-packages\rope\base\oi\soa.py", line 14, in analyze_module
    _analyze_node(pycore, pymodule, should_analyze, search_subscopes, followed_calls)
  File "...\Lib\site-packages\rope\base\oi\soa.py", line 20, in _analyze_node
    _analyze_node(
  File "...\Lib\site-packages\rope\base\oi\soa.py", line 37, in _analyze_node
    rope.base.ast.walk(child, visitor)
  File "...\Lib\site-packages\rope\base\ast.py", line 40, in walk
    return method(node)
           ^^^^^^^^^^^^
  File "...\Lib\site-packages\rope\base\oi\soa.py", line 120, in _Assign
    self._evaluate_assign_value(node, nodes)
  File "...\Lib\site-packages\rope\base\oi\soa.py", line 134, in _evaluate_assign_value
    pyobject = instance.get_object()
               ^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\rope\base\pynamesdef.py", line 44, in get_object
    result = self.pyfunction.get_parameter(self.index)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\rope\base\pyobjectsdef.py", line 76, in get_parameter
    if index < len(self.parameter_pyobjects.get()):
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\rope\base\utils\__init__.py", line 32, in newfunc
    return func(self, *args, **kwds)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\rope\base\pynames.py", line 187, in get
    self.set(self.get_inferred(*args, **kwds))
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\rope\base\pyobjectsdef.py", line 47, in _infer_parameters
    pyobjects = rope.base.oi.soi.infer_parameter_objects(self)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\rope\base\utils\__init__.py", line 47, in newfunc
    return func(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\rope\base\oi\soi.py", line 47, in infer_parameter_objects
    result = _parameter_objects(pyfunction)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\rope\base\oi\soi.py", line 135, in _parameter_objects
    type_ = hint_param(pyobject, name)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\rope\base\oi\type_hinting\providers\inheritance.py", line 20, in __call__
    result = self._delegate(superfunc, param_name)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\rope\base\oi\type_hinting\providers\composite.py", line 18, in __call__
    result = delegate(pyfunc, param_name)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\rope\base\oi\type_hinting\providers\docstrings.py", line 45, in __call__
    type_strs = self._parse_docstring(pyfunc.get_doc(), param_name)
                                      ^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\rope\base\pyobjects.py", line 217, in get_doc
    if isinstance(expr, ast.Expr) and isinstance(expr.value, ast.Str):
                                                             ^^^^^^^
AttributeError: module 'rope.base.ast' has no attribute 'Str'
dave-msk commented 2 months ago

I'll take a look

dave-msk commented 1 month ago

It turns out that the class ast.Str is removed from the standard library starting from Python 3.12. It requires an update on the tool dependencies. I'm working on it and will update this thread once it's complete

dave-msk commented 1 month ago

The fix has been pushed out. Please update to version 0.3.2 and try again, thanks

rtommy commented 1 month ago

The original fault is gone but I have a new fault now.

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "...\Scripts\merak.exe\__main__.py", line 7, in <module>
  File "...\Lib\site-packages\merak\main.py", line 34, in main
    app.run()
  File "...\Lib\site-packages\cement\core\foundation.py", line 948, in run
    return_val = self.controller._dispatch()
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\cement\ext\ext_argparse.py", line 810, in _dispatch
    return func()
           ^^^^^^
  File "...\Lib\site-packages\merak\commands\cythonize.py", line 57, in __call__
    success = cybuild.build_package_cython_extension(
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\merak\core\cybuild.py", line 76, in build_package_cython_extension
    mods, sub_pkgs = _restructure_package(tmp_proot, sep=sep)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\merak\core\cybuild.py", line 209, in _restructure_package
    move_cs = move.get_changes(pkg_rsrc)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\rope\refactor\move.py", line 530, in get_changes
    return self._calculate_changes(dest, resources, task_handle)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\rope\refactor\move.py", line 540, in _calculate_changes
    source = self._change_occurrences_in_module(dest, resource=module)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\rope\refactor\move.py", line 577, in _change_occurrences_in_module
    changed = self._change_import_statements(dest, new_name, module_imports)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\rope\refactor\move.py", line 624, in _change_import_statements
    self._handle_moving_in_from_import_stmt(
  File "...\Lib\site-packages\rope\refactor\move.py", line 651, in _handle_moving_in_from_import_stmt
    if import_stmt.import_info.get_imported_resource(context) == parent_module:
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\rope\refactor\importutils\importinfo.py", line 159, in get_imported_resource
    return context.project.find_relative_module(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\rope\base\project.py", line 170, in find_relative_module
    return _find_module_in_folder(folder, modname)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "...\Lib\site-packages\rope\base\project.py", line 448, in _find_module_in_folder
    if module.is_folder():
       ^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'is_folder'

I tried to build the customtkinter package.

dave-msk commented 1 month ago

Could you provide a sample package that reproduces the error when merak tries to build the binary?

rtommy commented 1 month ago

yes, I could. it's in my previous comment, last line. here it is again: I tried to build the customtkinter package.

dave-msk commented 1 month ago

I'll take a look

rtommy commented 1 month ago

any progress?

dave-msk commented 1 month ago

It appears that the absolufy_import library fails in a couple of legitimate use cases, and rope might not have taken care of the module moves properly. It ended up requiring a full refactoring of the code base to get rid of both libraries. The package restructuring logic is done, what's left is the wire-ups

dave-msk commented 1 month ago

The issue is fixed. Please upgrade to 0.3.3.1 and try again. Please note that Customtkinder's binary built by merak will not work properly due to the nature of module flattening as the project has code that tries to determine the package root via a chain of os.path.dirname to __file__ in modules not directly under the package root

rtommy commented 1 month ago

There are multiple .py files which indeed want to load other files (.ttf, .json, .ico) from the python package.

These use os.path.dirname, os.path.abspath and os.path.join functions.

But all of these considered under the package root and not somewhere else. They all wants to load a file from the assets package subfolder.

So would it help to rewrite the code and eliminate those functions or not?

Instead of script_directory = os.path.dirname(os.path.abspath(__file__)) we could use

import importlib.util
spec = importlib.util.find_spec(package)
script_directory = spec.submodule_search_locations[0]

Which would give us the package root absolute path:

>>> import importlib.util
>>> spec = importlib.util.find_spec("customtkinter")
>>> spec.submodule_search_locations[0]
'...\\Lib\\site-packages\\customtkinter'

And instead of

customtkinter_path = pathlib.Path(script_directory).parent.parent.parent
with open(os.path.join(customtkinter_path, "assets", "themes", f"{theme_name_or_path}.json"), "r") as f:

we could do string concatenation (use the script_directory above):

customtkinter_path = script_directory
with open("%s/%s/%s/%s.json" % (customtkinter_path, "assets", "themes", theme_name_or_path), "r") as f:

What do you think?

rtommy commented 1 month ago

The issue is fixed. Please upgrade to 0.3.3.1 and try again. Please note that Customtkinder's binary built by merak will not work properly due to the nature of module flattening as the project has code that tries to determine the package root via a chain of os.path.dirname to __file__ in modules not directly under the package root

I cannot even test it because it still drops an error right away after import and this error does not seem to have anything to do with the __file__ in modules:

>>> import customtkinter
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "customtkinter\\__init__.py", line 95, in init customtkinter
ModuleNotFoundError: No module named 'windows'
dave-msk commented 1 month ago

Thanks for reporting. I've patched the tool, please upgrade to v0.3.3.2 and try again

dave-msk commented 1 month ago

Regarding the package path, one can also define a module just under the main package and extract package location from there. Say, the module is defined in customtkinter/datalib.py and inside, one can write

import pathlib

PACKAGE_ROOT = pathlib.Path(__file__).resolve().parent

and use PACKAGE_ROOT to navigate to your expected package data location. For instance:

from customtkinter import datalib

theme_path = datalib.PACKAGE_ROOT.joinpath("assets", "themes", "blue.json")
rtommy commented 1 month ago

Thanks for reporting. I've patched the tool, please upgrade to v0.3.3.2 and try again

yes, it is better. Now it complains about those files to be imported from the package which we discussed it won't work properly and the related __file__ error: NameError: name '__file__' is not defined

This is ok for now, I need to raise an issue with customtkinter and see if the owner would change the code....

rtommy commented 1 month ago

Regarding the package path, one can also define a module just under the main package and extract package location from there. Say, the module is defined in customtkinter/datalib.py and inside, one can write

import pathlib

PACKAGE_ROOT = pathlib.Path(__file__).resolve().parent

and use PACKAGE_ROOT to navigate to your expected package data location. For instance:

from customtkinter import datalib

theme_path = datalib.PACKAGE_ROOT.joinpath("assets", "themes", "blue.json")

I have tested this but it does not work.

I just raised an issue on customtkinter #2614 where you can see a code change a proposed.

But now when I just tested it with merak, the pyd import still fails.... It seems it does not like __file__ at all so pathlib.Path(__file__) does not work either.

>>> import customtkinter
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "customtkinter\\__init__.py", line 95, in init customtkinter
  File "customtkinter\\___windows.py", line 2, in init customtkinter.___windows
  File "customtkinter\\___windows_ctk_tk.py", line 12, in init customtkinter.___windows_ctk_tk
  File "customtkinter\\___windows_widgets.py", line 3, in init customtkinter.___windows_widgets
  File "customtkinter\\___windows_widgets_ctk_button.py", line 15, in init customtkinter.___windows_widgets_ctk_button
  File "customtkinter\\___windows_widgets_theme.py", line 4, in init customtkinter.___windows_widgets_theme
  File "customtkinter\\___windows_widgets_theme_theme_manager.py", line 10, in init customtkinter.___windows_widgets_theme_theme_manager
  File "customtkinter\\___windows_widgets_utility.py", line 12, in init customtkinter.___windows_widgets_utility
NameError: name '__file__' is not defined

I will need to update my customtkinter issue once we sorted out this problem.

dave-msk commented 1 month ago

This seems to be related to CYTHON_PEP489_MULTI_PHASE_INIT=0 option. I'll take a look

dave-msk commented 1 month ago

I've tried removing the flag and this is what I see. It appears that I've passed the "customtkinter\\___windows_widgets_theme.py", line 4 mark and the error comes from line 9 & 11, so I guess this flag can be omitted without issues. I've also tried building rsa and it works fine as well. Please try it in v0.3.3.3

>>> import customtkinter
Traceback (most recent call last):
  File "customtkinter/___windows_widgets_theme.py", line 9, in customtkinter.___windows_widgets_theme
  File "customtkinter/___windows_widgets_theme_theme_manager.py", line 21, in customtkinter.___windows_widgets_theme_theme_manager.ThemeManager.load_theme
FileNotFoundError: [Errno 2] No such file or directory: '/Users/davidmuk/workspace/assets/themes/blue.json'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "customtkinter/__init__.py", line 95, in init customtkinter
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
  File "customtkinter/__init__.py", line 28, in customtkinter.__bootstrap._ExtensionLoader.exec_module
  File "customtkinter/__init__.py", line 22, in customtkinter.__bootstrap._ExtensionLoader.exec_module
  File "customtkinter/___windows.py", line 2, in init customtkinter.___windows
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
  File "customtkinter/__init__.py", line 28, in customtkinter.__bootstrap._ExtensionLoader.exec_module
  File "customtkinter/__init__.py", line 22, in customtkinter.__bootstrap._ExtensionLoader.exec_module
  File "customtkinter/___windows_ctk_tk.py", line 12, in init customtkinter.___windows_ctk_tk
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
  File "customtkinter/__init__.py", line 28, in customtkinter.__bootstrap._ExtensionLoader.exec_module
  File "customtkinter/__init__.py", line 22, in customtkinter.__bootstrap._ExtensionLoader.exec_module
  File "customtkinter/___windows_widgets.py", line 3, in init customtkinter.___windows_widgets
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
  File "customtkinter/__init__.py", line 28, in customtkinter.__bootstrap._ExtensionLoader.exec_module
  File "customtkinter/__init__.py", line 22, in customtkinter.__bootstrap._ExtensionLoader.exec_module
  File "customtkinter/___windows_widgets_ctk_button.py", line 15, in init customtkinter.___windows_widgets_ctk_button
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
  File "customtkinter/__init__.py", line 28, in customtkinter.__bootstrap._ExtensionLoader.exec_module
  File "customtkinter/__init__.py", line 22, in customtkinter.__bootstrap._ExtensionLoader.exec_module
  File "customtkinter/___windows_widgets_theme.py", line 11, in init customtkinter.___windows_widgets_theme
FileNotFoundError: [Errno 2] No such file or directory: '/Users/davidmuk/workspace/assets/themes/blue.json'
The .json theme file for CustomTkinter could not be found.
If packaging with pyinstaller was used, have a look at the wiki:
https://github.com/TomSchimansky/CustomTkinter/wiki/Packaging#windows-pyinstaller-auto-py-to-exe
rtommy commented 1 month ago

You have those errors because the assets folder is not under the expected path.... which is okay based on the current code which uses os.path... If you copy the assets subfolder under /Users/davidmuk/workspace then these errors should disappear.

For me this part works. I don't have those errors after copying assets folder where the python code expects.

I have a new error but it's not a problem to you, it is a minor fault in customtkinter:

>>> import customtkinter
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "customtkinter\\__init__.py", line 95, in init customtkinter
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
  File "customtkinter\\__init__.py", line 28, in customtkinter.__bootstrap._ExtensionLoader.exec_module
  File "customtkinter\\__init__.py", line 22, in customtkinter.__bootstrap._ExtensionLoader.exec_module
  File "customtkinter\\___windows.py", line 2, in init customtkinter.___windows
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
  File "customtkinter\\__init__.py", line 28, in customtkinter.__bootstrap._ExtensionLoader.exec_module
  File "customtkinter\\__init__.py", line 22, in customtkinter.__bootstrap._ExtensionLoader.exec_module
  File "customtkinter\\___windows_ctk_tk.py", line 12, in init customtkinter.___windows_ctk_tk
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
  File "customtkinter\\__init__.py", line 28, in customtkinter.__bootstrap._ExtensionLoader.exec_module
  File "customtkinter\\__init__.py", line 22, in customtkinter.__bootstrap._ExtensionLoader.exec_module
  File "customtkinter\\___windows_widgets.py", line 23, in init customtkinter.___windows_widgets
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
  File "customtkinter\\__init__.py", line 28, in customtkinter.__bootstrap._ExtensionLoader.exec_module
  File "customtkinter\\__init__.py", line 22, in customtkinter.__bootstrap._ExtensionLoader.exec_module
  File "customtkinter\\___windows_widgets_ctk_label.py", line 298, in init customtkinter.___windows_widgets_ctk_label
TypeError: Expected unicode, got bool

The problematic code is in bind() of windows/widgets/ctk.label.py:

    def bind(self, sequence: str = None, command: Callable = None, add: str = True):
        """ called on the tkinter.Label and tkinter.Canvas """
        if not (add == "+" or add is True):
            raise ValueError("'add' argument can only be '+' or True to preserve internal callbacks")
        self._canvas.bind(sequence, command, add=True)
        self._label.bind(sequence, command, add=True)

add is considered either string or bool which is not entirely correct here... If I change add: str = True to add: str = Union[str, bool] = True, then I get zero error, import works.

But.... when I try to use the pyd file, it fails unfortunately... Please try it with examples/complex_example.py.

Traceback (most recent call last):
  File "complex_example.py", line 163, in <module>
    app = App()
          ^^^^^
  File "complex_example.py", line 23, in __init__
    self.sidebar_frame = customtkinter.CTkFrame(self, width=140, corner_radius=0)
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "customtkinter\\___windows_widgets_ctk_frame.py", line 49, in customtkinter.___windows_widgets_ctk_frame.CTkFrame.__init__
  File "customtkinter\\___windows_widgets_core_widget_classes_ctk_base_class.py", line 90, in customtkinter.___windows_widgets_core_widget_classes_ctk_base_class.CTkBaseClass.__init__
  File "customtkinter\\___windows_widgets_core_widget_classes_ctk_base_class.py", line 221, in customtkinter.___windows_widgets_core_widget_classes_ctk_base_class.CTkBaseClass._detect_color_of_master
AttributeError: module 'customtkinter.___windows' has no attribute 'widgets'

The file: customtkinter/windows/widgets/core_widget_classes/ctk_base_class.py Function: _detect_color_of_master(...)

dave-msk commented 1 month ago

I've tried importing directly from the package source (in my cloned CustomTkinter/ copy) to see if it works and I've got segmentation fault. Is there any way I can try out the expected behavior?

>>> import customtkinter
Segmentation fault: 11
rtommy commented 1 month ago

I don’t have segmentation fault, import works for me. That’s strange…

Please check the example file I shared earlier and based on that you can test.

complex_example.py

dave-msk commented 1 month ago

I've also inspected the file customtkinter/windows/__init__.py and I don't see any import for the widget subpackage. I don't quite understanding how this is working for others

dave-msk commented 1 month ago

I don’t have segmentation fault, import works for me. That’s strange… Please check the example file I shared earlier and based on that you can test. complex_example.py

Tried installing customtkinter and running the example, and this is what I've got

$ python complex_example.py 
Segmentation fault: 11

Is this OS-dependent? This seems to be targeting for Windows and I don't have a Windows machine to test this right now

rtommy commented 1 month ago

I don’t have segmentation fault, import works for me. That’s strange… Please check the example file I shared earlier and based on that you can test. complex_example.py

Tried installing customtkinter and running the example, and this is what I've got

$ python complex_example.py 
Segmentation fault: 11

Is this OS-dependent? This seems to be targeting for Windows and I don't have a Windows machine to test this right now

According to the customtkinter website Windows, Linux and MacOS are supported:

CustomTkinter is a python UI-library based on Tkinter, which provides new, modern and fully customizable widgets. They are created and used like normal Tkinter widgets and can also be used in combination with normal Tkinter elements. The widgets and the window colors either adapt to the system appearance or the manually set mode ('light', 'dark'), and all CustomTkinter widgets and windows support HighDPI scaling (Windows, macOS). With CustomTkinter you'll get a consistent and modern look across all desktop platforms (Windows, macOS, Linux).

I have never seen Segmentation fault with customtkinter before.

I use Windows 11 by the way.

rtommy commented 1 month ago

I've also inspected the file customtkinter/windows/__init__.py and I don't see any import for the widget subpackage. I don't quite understanding how this is working for others

I think the problem is within the ctk_base_class.py. If you have a look of this: def _detect_color_of_master()

You can see it has the following if-else condition:

        if isinstance(master_widget, (windows.widgets.core_widget_classes.CTkBaseClass, windows.CTk, windows.CTkToplevel, windows.widgets.ctk_scrollable_frame.CTkScrollableFrame)):
            if master_widget.cget("fg_color") is not None and master_widget.cget("fg_color") != "transparent":
                return master_widget.cget("fg_color")

            elif isinstance(master_widget, windows.widgets.ctk_scrollable_frame.CTkScrollableFrame):
                return self._detect_color_of_master(master_widget.master.master.master)

I guess the windows.widgets or windows.* parts could cause the problem we have.... AttributeError: module 'customtkinter.___windows' has no attribute 'widgets'

UPDATE: have a look of the imports in the beginning of that file... Not sure if this conflicting with your scripts...

from .... import windows  # import windows for isinstance checks
dave-msk commented 1 month ago

I guess the windows.widgets or windows.* parts could cause the problem we have.... AttributeError: module 'customtkinter.___windows' has no attribute 'widgets'

Yes, the issue we hit is that windows does have a subpackage named widgets, but it is not imported in customtkinter/windows/__init__.py. Therefore the interpreter is not able to find that name/identifier in windows. That looks reasonable to me. That's why I wanted to try out the library to see how it is able to refer to windows.widgets despite widgets is not imported under windows's init file, but I've got segmentation fault, no matter how I imported the library (I'm on Mac)

UPDATE: have a look of the imports in the beginning of that file... Not sure if this conflicting with your scripts...

from .... import windows  # import windows for isinstance checks

I don't see any conflicts here. All Merak does is to transform it to absolute import (from customtkinter import windows) and perform module renaming windows -> ___windows

rtommy commented 1 month ago

If you have another look at the package structure, you can see that every folder (subfolder) has a __init__.py included.

So this means to me that if you import windows with

from .... import windows  # import windows for isinstance checks

and after that you refer to anything under it like windows.widgets.xxx then it will work since widgets folder has it's own __init__.py.

This means you don't need to import widgets per se, as you expect.

Basically, every folder is a (sub)package...

Does this make sense to you?

dave-msk commented 4 weeks ago

There seems to be some misunderstanding on how the import system works. Please refer to the documentation on how it works

Basically, when an import-from statement is executed, the interpreter loads all the packages along the module path, and binds a name defined by that statement to the module itself.

from a.b.c import d
# Loads "a" -> "a.b" -> "a.b.c" -> "a.b.c.d"
# Binds the name "d" to the module/package/attribute "a.b.c.d"

The names of a package that one can access through "." is defined in its __init__.py file. If such name is not defined in it, even though b is a subpackage of a, b is still not accessible via . from the name a, despite it is loaded in the memory

5.2.1. Regular packages¶

Python defines two types of packages, regular packages and namespace packages. Regular packages are traditional packages as they existed in Python 3.2 and earlier. A regular package is typically implemented as a directory containing an init.py file. When a regular package is imported, this init.py file is implicitly executed, and the objects it defines are bound to names in the package’s namespace. The init.py file can contain the same Python code that any other module can contain, and Python will add some additional attributes to the module when it is imported.

rtommy commented 4 weeks ago

I am a simple user and not the developer of customtkinter. 😄

The original package works, I use it for my application. So I assume the code is fine. Yes, perhaps I might misunderstand some logic of python overall but I was just simply trying to help you to fix the issue.

Unfortunately, I cannot really troubleshoot your tool hence, I don't know why that error occurs with the binary package. But to me it seems it's related to package import and when it's interpreted in your code.

If you add more debug to your code, I can compile it again with -vv and then share the logs, if that helps.

dave-msk commented 4 weeks ago

There might be a possibility that the import system implicitly added names to the package itself. This requires further experiments to verify its behavior.

I'm not sure how it's working for you as I'm seeing segmentation fault when trying to import customtkinter, so the expected behavior of this library is not observable from my side. I'll need a working copy of it before I'm able to understand what has been happening underneath

rtommy commented 4 weeks ago

This is just an assumption but I think the segmentation fault occurs for you on Linux only, for me on Windows the cythonized compilation can be imported as expected.

I'm not sure if you could debug/troubleshoot your compilation on Linux to figure out the root cause of the segmentation fault.

dave-msk commented 2 weeks ago

It appears that the import system will implicitly set attributes to the parent module/package. Please update to v0.3.4 and try again

P.S. customtkinter is still giving me seg fault. This has nothing to do with merak as I hit that with a clean python environment with customtkinter installed by pip install

rtommy commented 2 weeks ago

Okay, so at least we know something else is not exactly correct in the code which causes seg fault on Linux. Not sure how to troubleshoot that though.

I have tried v0.3.4 and it seems better but overall it's still not working because the original python code has a lot of type casting issues which by default working because of Python Implicit Type Conversion. But it seems after the binary compilations, all of these ends up with raising a TypeError.

Exception in Tkinter callback
Traceback (most recent call last):
  File "...\Lib\tkinter\__init__.py", line 1968, in __call__
    return self.func(*args)
           ^^^^^^^^^^^^^^^^
  File "customtkinter\\___windows_widgets_ctk_scrollbar.py", line 292, in customtkinter.___windows_widgets_ctk_scrollbar.CTkScrollbar.set
TypeError: must be real number, not str

or

Traceback (most recent call last):
  File "complex_example.py", line 163, in <module>
    app = App()
          ^^^^^
  File "complex_example.py", line 59, in __init__
    self.tabview.add("CTkTabview")
  File "customtkinter\\___windows_widgets_ctk_tabview.py", line 420, in customtkinter.___windows_widgets_ctk_tabview.CTkTabview.add
  File "customtkinter\\___windows_widgets_ctk_tabview.py", line 405, in customtkinter.___windows_widgets_ctk_tabview.CTkTabview.insert
  File "customtkinter\\___windows_widgets_ctk_segmented_button.py", line 444, in customtkinter.___windows_widgets_ctk_segmented_button.CTkSegmentedButton.insert
  File "customtkinter\\___windows_widgets_ctk_segmented_button.py", line 205, in customtkinter.___windows_widgets_ctk_segmented_button.CTkSegmentedButton._create_button
  File "customtkinter\\___windows_widgets_ctk_button.py", line 164, in customtkinter.___windows_widgets_ctk_button.CTkButton.__init__
  File "customtkinter\\___windows_widgets_ctk_button.py", line 254, in customtkinter.___windows_widgets_ctk_button.CTkButton._draw
  File "customtkinter\\___windows_widgets_core_rendering_draw_engine.py", line 139, in customtkinter.___windows_widgets_core_rendering_draw_engine.DrawEngine.draw_rounded_rect_with_border
TypeError: Argument 'width' has incorrect type (expected int, got float)

So I'm not sure if there is way to still allow/use/force Python Implicit Type Conversion in the binary compilation?

Or since this is compiled to C code with cythonize, we need to actually correct the original code and do Explicit Type Conversion?

rtommy commented 1 week ago

What Linux distro you have? Python version?

I just tried on MacOS Sequoia 15.1 with Python 3.12 and customtkinter complex_example.py works.

% python Downloads/complex_example.py
2024-11-14 14:37:19.474 Python[78105:4348550] +[IMKClient subclass]: chose IMKClient_Modern
2024-11-14 14:37:19.474 Python[78105:4348550] +[IMKInputSession subclass]: chose IMKInputSession_Modern
image