Re: Incomplete sys.path with embeddable python (Windows)!?
Am 21.04.2023 um 18:07 schrieb Thomas Passin: On 4/20/2023 5:47 PM, Ralf M. wrote: Hello, when I run a script with a "normally" installed python, the directory the script resides in is automatically added as first element to sys.path, so that "import my_local_module" finds my_local_module.py in the directory of the script. However, when I run the same script with embeddable python ("Windows embeddable package (64-bit)", download link https://www.python.org/ftp/python/3.11.3/python-3.11.3-embed-amd64.zip) the script directory is *not* prepended to the path, thus "import my_local_module" gives an ImportError. I couldn't find an option to get the "normal" behaviour. Any ideas how to do that? What I tried so far: [...] * I can add the following lines to every script: import sys script_path = __file__.rsplit("\\", 1)[0] if script_path not in sys.path: sys.path[0:0] = [script_path] import my_local_modul [...] Thank your for your hints. I haven't worked with embeddable python, but here are some possibilities that came to mind, depending on how your system works - 1. If your script is started from the command line, sys.argv[0] gives the path to the script; I didn't think of sys.argv[0] to get at the path; this might be quite useful, I'll try it out next week. You could use os.path.dirname() to get its directory. This will end up the same place as your code fragment, but looks nicer and handles different path separators (e.g., Linux vs Windows); Yes, but it requires another import and the embedded package is only available for windows anyway, I think. I'll consider the idea, though. 2. You could write a little module that figures out the script's path and import that first in all your scripts. 3. If you know all the directories that your scripts will be in, you could add them all to a xx.pth file (do a search to make sure where to put .pth files for an embeddable case). I thought about that, but for that to work all local modules across all script locations must have unique names, otherwise import might get hold of a module from the wrong directory. Certainly doable for a few scripts, but might become a source of hard to track errors when the number of scripts increases and later maintainers are not aware of the naming restriction. [...} -- https://mail.python.org/mailman/listinfo/python-list
Re: Incomplete sys.path with embeddable python (Windows)!?
Am 22.04.2023 um 03:27 schrieb Greg Ewing via Python-list: How are you invoking your script? Presumably you have some code in your embedding application that takes a script path and runs it. Instead of putting the code to update sys.path into every script, the embedding application could do it before running the script. In principle a good idea, but I don't know how to do that: The script is currently invoked by a .cmd file, but that may change to a shortcut (.lnk). This is what the embeddable package documentation calls "Python Application - simple approach". To update sys.path on start up I would need to do something like C:\path\to\python.exe --add-path C:\s-path C:\s-path\script.py but I couldn't find an option like --add-path. -- https://mail.python.org/mailman/listinfo/python-list
Re: Incomplete sys.path with embeddable python (Windows)!?
Am 21.04.2023 um 17:31 schrieb Mats Wichmann: On 4/20/23 15:47, Ralf M. wrote: Hello, when I run a script with a "normally" installed python, the directory the script resides in is automatically added as first element to sys.path, so that "import my_local_module" finds my_local_module.py in the directory of the script. However, when I run the same script with embeddable python ("Windows embeddable package (64-bit)", download link https://www.python.org/ftp/python/3.11.3/python-3.11.3-embed-amd64.zip) the script directory is *not* prepended to the path, thus "import my_local_module" gives an ImportError. This is intended behavior - the question comes up from time to time. The embeddable distribution is intended to be part of an application, not a general-purpose Python you can call for just anything. There are a bunch of details here, for example: https://github.com/python/cpython/issues/79022 Thank you for the pointer to the issue. I'll try to remove the ._pth completely (and see whether that breaks anything) and may have a look at the nuget.org package. I can see that for many cases the behaviour is appropriate, but I had hoped that there is a configuration option for the cases where it is not. About my use case: There is a complex application package, consisting of some commercial and some freeware software, tied together with scripts in at least four different scripting languages. Now I intend to add further functionality in a fifth language, Python. The idea is to make the embeddedable package part of the application package and have scripts for the new functions. Several independent functions are to be added, each consisting of a script plus some local modules, and all of them should use the same python embedded into the application package. -- https://mail.python.org/mailman/listinfo/python-list
Incomplete sys.path with embeddable python (Windows)!?
Hello, when I run a script with a "normally" installed python, the directory the script resides in is automatically added as first element to sys.path, so that "import my_local_module" finds my_local_module.py in the directory of the script. However, when I run the same script with embeddable python ("Windows embeddable package (64-bit)", download link https://www.python.org/ftp/python/3.11.3/python-3.11.3-embed-amd64.zip) the script directory is *not* prepended to the path, thus "import my_local_module" gives an ImportError. I couldn't find an option to get the "normal" behaviour. Any ideas how to do that? What I tried so far: * The start-up value for sys.path seems to be defined in python311._pth. It looks that I can add further static paths to it, but I don't know how to make it add the script path (which can be different for different scripts). * Uncommenting "import site" in python311._pth doesn't help. * It seems that I could import something else in python311._pth, but I don't know how something imported from there could find out the path of the script that is about to be started. * I read the (rather short) documentation of the embeddable package and of the site module several times but couldn't recognize a hint as to how to solve the issue. * I can add the following lines to every script: import sys script_path = __file__.rsplit("\\", 1)[0] if script_path not in sys.path: sys.path[0:0] = [script_path] import my_local_modul That works, but it's ugly, executing code between imports is frowned upon, and it needs to be added to every script. Does anybody have a better idea? Any help is appreciated. Ralf M. -- https://mail.python.org/mailman/listinfo/python-list
Re: How to replace an instance method?
Am 17.09.2022 um 00:35 schrieb Dan Stromberg: On Fri, Sep 16, 2022 at 2:06 PM Ralf M. <mailto:ral...@t-online.de>> wrote: I would like to replace a method of an instance, but don't know how to do it properly. You appear to have a good answer, but... are you sure this is a good idea? It's definitely a dirty hack. It'll probably be confusing to future maintainers of this code, and I doubt static analyzers will like it either. I agree that I will have to add sufficient comments for the future maintainer, should there ever be one (and even for me to still understand it next year). I don't use static analyzers. I'm not the biggest fan of inheritance you'll ever meet, but maybe this is a good place for it? Using a derived version of the class in question to overwrite the method was my first idea, however I don't instantiate the class in question myself, it is instantiated during the initialisation of another class, so I would at least have to derive a modified version of that as well. And that code is rather complex, with metaclasses and custom decorators, and I feel uncomfortable messing with that, while the method I intend to change is quite simple and straightforward. In case anybody is interested what I'm trying to achieve: It's simple in pandas to read an excel file into a dataframe, but only the cell value is read. Sometimes I need more / other information, e.g. some formatting or the hyperlink in a cell. Reopening the file with openpyxl and getting the info is possible, but cumbersome. Looking into the pandas code for reading excel files (which uses openpyxl internally) I noticed a method (of an internal pandas class) that extracts the value from an openpyxl cell. This method is rather simple and seems the ideal spot to change to get what I want. My idea is to instantiate pandas.ExcelFile (official pandas API), get the reader instance (an attribute of the ExcelFile object) and modify the method of the reader instance. The fact that the method I change and the ExcelFile attribute containing the reader are both private (start with _) doesn't make it any better, but I'm desperate enough to be willing to adapt my code to every major pandas release, if necessary. Ralf M. -- https://mail.python.org/mailman/listinfo/python-list
Re: How to replace an instance method?
Am 16.09.2022 um 23:34 schrieb Eryk Sun: On 9/16/22, Ralf M. wrote: I would like to replace a method of an instance, but don't know how to do it properly. A function is a descriptor that binds to any object as a method. For example: >>> f = lambda self, x: self + x >>> o = 42 >>> m = f.__get__(o) >>> type(m) >>> m.__self__ is o True >>> m(10) 52 Thank you and Chris A. for the two suggestions how to replace a method. I tried both inst.method = functools.partial(new_method, inst) and inst.method = new_method.__get__(inst) and both work in my toy example. I will try it on the real code next week. Even though the functools.partial solution is easier to understand (at least for me), I will probably use the __get__ solution as it avoids the import of an extra library. Ralf M. -- https://mail.python.org/mailman/listinfo/python-list
How to replace an instance method?
I would like to replace a method of an instance, but don't know how to do it properly. My first naive idea was inst = SomeClass() def new_method(self, param): # do something return whatever inst.method = new_method however that doesn't work: self isn't passed as first parameter to the new inst.method, instead inst.method behaves like a static method. I had a closer look at the decorators classmethod and staticmethod. Unfortunetely I couldn't find a decorator / function "instancemethod" that turns a normal function into an instancemethod. The classmethod documentation contains a reference to the standard type hierarchie, and there is an explanation that an instancemethod is sort of a dynamically created wrapper around a function, which is accessable as __func__. So I modified the last line of the example above to inst.method.__func__ = new_method but got told that __func__ is read only. I found some information about methods in the Descriptor HowTo Guide, but it's about how it works internally and doesn't tell how to solve my problem (at least it doesn't tell me). Now I'm running out of ideas what to try next or what sections of the documentation to read next. Any ideas / pointers? Ralf M. -- https://mail.python.org/mailman/listinfo/python-list
Re: .0 in name
Am 13.05.2022 um 23:23 schrieb Paul Bryan: On Sat, 2022-05-14 at 00:47 +0800, bryangan41 wrote: May I know (1) why can the name start with a number? The name of an attribute must be an identifier. An identifier cannot begin with a decimal number. I'm not sure about the first statement. Feeding [print("locals:", locals()) or c for c in "ab"] to the REPL, the result is locals: {'.0': , 'c': 'a'} locals: {'.0': , 'c': 'b'} ['a', 'b'] i.e. there is a variable of name .0 in the local namespace within the list comprehension, and .0 is definitely not an identifier. I came across this while investigating another problem with list comprehensions, and I think the original post was about list comprehensions. There also can be non-identifier names in the global namespace and as attributes, e.g. using the REPL again: globals()["42"] = "The Answer" globals() outputs (see last entry) {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': , '__spec__': None, '__annotations__': {}, '__builtins__': (built-in)>, '42': 'The Answer'} and class Cls: def __init__(self, lst): for i, e in enumerate(lst): self.__dict__[str(i)] = e obj = Cls([31, 42, 53]) getattr(obj, "1") works and outputs 42 (2) where in the doc is it?! https://docs.python.org/3/reference/lexical_analysis.html#identifiers That refers to identifiers, i.e. names that are recognised as such by the lexer, i.e. that can be written directly in Python source code. As shown above, names that are not identifiers can be used in several namespaces and as attributes. It's just a bit harder to use non-identifier names than identifiers. Whether it's a good idea to use them at all is a different question. I think the OP wondered about the .0 in the local namespace within list comprehensions. Unfortunately I cannot say much about that. Paul Ralf M. -- https://mail.python.org/mailman/listinfo/python-list
Re: "py.ini" question
Am 25.04.2021 um 16:30 schrieb Mats Wichmann: On 4/24/21 2:57 PM, Chris Angelico wrote: On Sun, Apr 25, 2021 at 5:57 AM Gisle Vanem wrote: With 'py -3.6' or 'py 3.8' I get the expected. But with 'py -3': Python 3.8.9 (default, Apr 13 2021, 15:54:59) [GCC 10.2.0 64 bit (AMD64)] on win32 I believe that's because you're asking for "the latest in the 3.x series". unless differently described by the ini file, which is what the OP is trying to do. I just added a 3.10 alpha to my Windows setup (I don't do my programming there, so there hadn't been any need), and set up an ini file to leave 3.9 as the default and all works as expected - py -0, py, py -3, py -3.10 all given me the one I would expect to get based on that setup (-0 shows 3.9-64 starred, despite 3.10-64 being "the latest). Personally stumped why it's not working for Gisle. I think the problem / misunderstanding is that Gisle put in py.ini only [defaults] python=3.6 and expected this to make py -3 start 3.6. However py -3 looks for a key named 'python3' and, not finding it, uses default behaviour (ignoring the 'python' key), i.e. starts the most modern stable 3.x. The py.ini documentation is a bit hard to find in the help file and hard to understand as the interesting part talks about environment variables and shebang line commands, not py.ini settings and py.exe parameters. However, the behaviour is the same in both cases and the relevant example is: "If PY_PYTHON=3.1-32, the command python will use the 32-bit implementation of 3.1 whereas the command python3 will use the latest installed Python (PY_PYTHON was not considered at all as a major version was specified.)" I tried the (rather insane) py.ini [defaults] python=3.7 python2=3.8 python4=2.7 and it works as documented (py -3 shows default behaviour as there is no 'python3' in py.ini): C:\>py Python 3.7.4 (tags/v3.7.4:e09359112e, Jul 8 2019, 20:34:20) [MSC v.1916 64 bit(AMD64)] on win32 C:\>py -2 Python 3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:57:54) [MSC v.1924 64 bit (AMD64)] on win32 C:\>py -3 Python 3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:57:54) [MSC v.1924 64 bit (AMD64)] on win32 C:\>py -4 Python 2.7.18 (v2.7.18:8d21aa21f2, Apr 20 2020, 13:19:08) [MSC v.1500 32 bit (Intel)] on win32 -- https://mail.python.org/mailman/listinfo/python-list
unittest test discovery: regular packages vs. namespace packages
Hello, to my last question I got many helpful and enlightening answers. So I try again with a completely different topic. https://docs.python.org/3/library/unittest.html#test-discovery says about test discovery: "Unittest supports simple test discovery. In order to be compatible with test discovery, all of the test files must be modules or packages (including namespace packages) importable from the top-level directory of the project (this means that their filenames must be valid identifiers). [...] Note: As a shortcut, python -m unittest is the equivalent of python -m unittest discover." Therefore I expected python -m unittest to run all tests in the current directory and its subdirectories, regardless of whether the subdirectories contain a __init__.py or not. However, this only works for me if an (empty) __init__.py is present, i.e. the package is a regular one. As soon as I delete the __init__.py, turning the regular package into a namespace package, unittest doesn't find the test files any more. When I recreate __init__.py, the test file (e.g. test_demo.py) is found again. See demo session further down. I tried the following Python versions, all showed the same behavior: Python 3.7.1 (python.org) on Win10 Python 3.7.4 (python.org) on Win7 Python 3.7.7 (Anaconda) on Win10 Python 3.8.3 (Anaconda) on Win10 What am I missing / misunderstanding? Demo session: F:\demo>dir /s /b F:\demo\test_we4n7uke5vx F:\demo\test_we4n7uke5vx\test_demo.py F:\demo\test_we4n7uke5vx\__init__.py F:\demo>type test_we4n7uke5vx\__init__.py F:\demo>type test_we4n7uke5vx\test_demo.py import unittest class SomeTestCase(unittest.TestCase): def test_fail_always(self): self.assertTrue(False) F:\demo>py -m unittest F == FAIL: test_fail_always (test_we4n7uke5vx.test_demo.SomeTestCase) -- Traceback (most recent call last): File "F:\demo\test_we4n7uke5vx\test_demo.py", line 4, in test_fail_always self.assertTrue(False) AssertionError: False is not true -- Ran 1 test in 0.001s FAILED (failures=1) F:\demo>del test_we4n7uke5vx\__init__.py F:\demo>py -m unittest -- Ran 0 tests in 0.000s OK F:\demo>echo # > test_we4n7uke5vx\__init__.py F:\demo>py -m unittest F == FAIL: test_fail_always (test_we4n7uke5vx.test_demo.SomeTestCase) -- Traceback (most recent call last): File "F:\demo\test_we4n7uke5vx\test_demo.py", line 4, in test_fail_always self.assertTrue(False) AssertionError: False is not true -- Ran 1 test in 0.001s FAILED (failures=1) F:\demo> -- https://mail.python.org/mailman/listinfo/python-list
Enums are Singletons - but not always?
Hello, recently I wrote a small library that uses an Enum. That worked as expected. Then I added a main() and if __name__ == "__main__" to make it runable as script. Now Enum members that should be the same aren't identical any more, there seem to be two instances of the same Enum. I think I know what's going on, but cannot find a good and elegant way to avoid the problem. I hope someone here can help me there. Below are a simplified code sample, the results when I run it and my thoughts. # Code of mod1.py # import enum, mod2 class En(enum.Enum): A = 1 B = 2 def main(): a = mod2.getA() print("a is En.A:", a is En.A) print("a:", repr(a), "En.A:", repr(En.A)) print("id(a), id(a.__class__)", id(a), id(a.__class__)) print("id(En.A), id(En) ", id(En.A), id(En)) if __name__ == "__main__": main() # End of mod1.py # # Code of mod2.py # import mod1 def getA(): return mod1.En.A # End of mod2.py # # Results when run: # C:\tmp>py mod1.py a is En.A: False a: En.A: id(a), id(a.__class__) 33305864 7182808 id(En.A), id(En) 33180552 7183752 C:\tmp>py Python 3.7.4 (tags/v3.7.4:e09359112e, Jul 8 2019, 20:34:20) [MSC v.1916 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> import mod1 >>> mod1.main() a is En.A: True a: En.A: id(a), id(a.__class__) 49566792 44574280 id(En.A), id(En) 49566792 44574280 >>> So: When run as script there are two instances of En (different ids), but when mod1 is imported and mod1.main() is run it works as expected (just one instance of En, same ids). BTW: py -m mod1 doesn't work either. What I thing is happening: When the script is run, mod1.py is executed and an instance of En and its members is created. During the same run mod1 is also imported (via mod2), the file mod1.py is executed again as part of the import and another, different instance of En and its members is created. How do I have to change mod1.py to avoid the problem? Currently I have moved main() into a new file script.py. That works, but is not what I wanted. I doubt it's a bug in the enum module, but should that be the case, I'm willing to open an issue on the bug tracker. Or can nothing be done about it? Looking forward to any ideas Ralf M. P.S.: As I was about to send this post the following modification occured to me (see below). It works, but it doesn't feel right to import a module directly from inside itself. # Modified code of mod1.py (one line added) # import enum, mod2 class En(enum.Enum): A = 1 B = 2 from mod1 import En # NEW LINE, overwrite just defined En def main(): a = mod2.getA() print("a is En.A:", a is En.A) print("a:", repr(a), "En.A:", repr(En.A)) print("id(a), id(a.__class__)", id(a), id(a.__class__)) print("id(En.A), id(En) ", id(En.A), id(En)) if __name__ == "__main__": main() -- https://mail.python.org/mailman/listinfo/python-list
Re: Spread a statement over various lines
Am 18.09.2019 um 22:24 schrieb Alexandre Brault: On 2019-09-18 4:01 p.m., Ralf M. wrote: I don't know the exact rules of Windows wildcards, so there may be even more cases of unexpected behavior. If anyone knows where to find the complete rules (or a python module that implements them), I would be interested. fnmatch in the standard library has a translate function that transforms a glob pattern to a regex https://docs.python.org/3.7/library/fnmatch.html#fnmatch.translate Alex Thank you for the pointer. However, from the documentation of module fnmatch: "This module provides support for Unix shell-style wildcards" And Unix shell-style wildcards differ from Windows cmd wildcards. For one, [ ] are special in Unix shells, but not in Windows cmd. For another, cmd wildcards have several quirks, e.g. *.* matching filenames that don't contain a dot, or "a???" matching "ab". Several years ago, when I needed DOS-style globbing, I copied fnmatch.py and glob.py from the standard library and modified them to treat [ ] as not special. However, that didn't help with the cmd quirks, and I don't even know all the rules about cmd wildcards. Ralf -- https://mail.python.org/mailman/listinfo/python-list
Re: Spread a statement over various lines
Am 18.09.2019 um 22:22 schrieb Chris Angelico: On Thu, Sep 19, 2019 at 6:20 AM Ralf M. wrote: Am 17.09.2019 um 20:59 schrieb Manfred Lotz: I have a function like follows def regex_from_filepat(fpat): rfpat = fpat.replace('.', '\\.') \ .replace('%', '.') \ .replace('*', '.*') return '^' + rfpat + '$' As I don't want to have the replace() functions in one line my question is if it is ok to spread the statement over various lines as shown above, or if there is a better way? Thanks. Not related to your question, but: You seem to try to convert a Windows wildcard pattern to a regex pattern. However, wildcards sometimes behave a bit different than what you assume. I know for instance that *.* matches any filename, even if the filename doesn't contain a dot. Hmm, why do you assume it's a Windows wildcard pattern specifically? ChrisA I think I jumped to that conclusion because the example didn't treat [ ] as special characters. [ ] are special in a unix shell, but not at a cmd prompt. Thinking it over, [ ] would need a much differnt treatment and might be left out of the example for that reason, though. Also I may be biased: I mostly use Windows, Linux only occasionally. Ralf M. -- https://mail.python.org/mailman/listinfo/python-list
Re: Spread a statement over various lines
Am 17.09.2019 um 20:59 schrieb Manfred Lotz: I have a function like follows def regex_from_filepat(fpat): rfpat = fpat.replace('.', '\\.') \ .replace('%', '.') \ .replace('*', '.*') return '^' + rfpat + '$' As I don't want to have the replace() functions in one line my question is if it is ok to spread the statement over various lines as shown above, or if there is a better way? Thanks. Not related to your question, but: You seem to try to convert a Windows wildcard pattern to a regex pattern. However, wildcards sometimes behave a bit different than what you assume. I know for instance that *.* matches any filename, even if the filename doesn't contain a dot. Out of curiosity I played around a bit, details below. As you can see, there are other wildcard strangenesses, e.g. - ? does not match a dot - between letters etc. matches exactly 4 characters, but at the end or directly before a dot matches at most 4 characters I don't know the exact rules of Windows wildcards, so there may be even more cases of unexpected behavior. If anyone knows where to find the complete rules (or a python module that implements them), I would be interested. Regards, Ralf - Details (Win 7 home SP1) - C:\tmp>more pydirb.py #!/usr/bin/env python3 import os, re, sys def regex_from_filepat(fpat): rfpat = fpat.replace('.', '\\.') \ .replace('?', '.') \ .replace('*', '.*') return '^' + rfpat + '$' regexfilepat = re.compile(regex_from_filepat(sys.argv[1])) for name in os.listdir(): if regexfilepat.match(name): print(name) C:\tmp>dir /b * foo foo.bar foo.bar.c foo.c pydirb.py C:\tmp>pydirb * foo foo.bar foo.bar.c foo.c pydirb.py C:\tmp>dir /b *.* foo foo.bar foo.bar.c foo.c pydirb.py C:\tmp>pydirb *.* foo.bar foo.bar.c foo.c pydirb.py C:\tmp>dir /b *.*.*.*.* foo foo.bar foo.bar.c foo.c pydirb.py C:\tmp>pydirb *.*.*.*.* C:\tmp>dir /b foo.? foo foo.c C:\tmp>pydirb foo.? foo.c C:\tmp>dir /b foo. foo foo.bar foo.c C:\tmp>pydirb foo. C:\tmp>dir /b foo?bar Datei nicht gefunden C:\tmp>pydirb foo?bar foo.bar C:\tmp>dir /b f?o.bar foo.bar C:\tmp>pydirb f?o.bar foo.bar C:\tmp>dir /b f??o.bar Datei nicht gefunden C:\tmp>pydirb f??o.bar C:\tmp>dir /b fo?.bar foo.bar C:\tmp>pydirb fo?.bar foo.bar C:\tmp>dir /b fo??.bar foo.bar C:\tmp>pydirb fo??.bar C:\tmp>dir /b foo??.bar foo.bar C:\tmp>pydirb foo??.bar -- https://mail.python.org/mailman/listinfo/python-list
Multidimensional dicts
Recently I wrote a quick and dirty script to do some counting and statistics. When I re-read it a bit later I noticed that I had been using two different ways to create two-dimensional (default-)dicts. Now I'm wondering whether one of them is "better" or more pythonic than the other. What I did: ddd_a = collections.defaultdict(set) ddd_a[(key1, key2)].add(foo) ddd_b = collections.defaultdict(lambda: collections.defaultdict(set)) ddd_b[key1][key2].add(foo) Both work as expected. Trying to think about differences I only noticed that ddd_a more easily generalises to more dimensions, and ddd_b has the benefit that ddd_b[key1] is a dict, which might help if one "row" needs to be fed to a function that expects a dict. More general ddd_a looks more symmetric (key1 and key2 are exchangeable, if done consistently) and ddd_b looks more hierarchic (like a tree traversed from root to leaves where key1, key2 etc. determine which way to go at each level). ddd_b also is more simmilar to how two-dimensional lists are done in python. Any recommendations / comments as to which to prefer? -- https://mail.python.org/mailman/listinfo/python-list