o3bvv / lazy-string

Python library for defining strings with delayed evaluation

Home Page:https://pypi.org/project/lazy-string

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

LazyString.encode fails with <LazyString broken>

bdnettleton opened this issue · comments

The following code snippet raises a TypeError: 'bytes' object is not callable

from lazy_string import LazyString

def get_abc():
    return "abc"

s = LazyString(get_abc)

print(s.encode('UTF-8'))

Here is the traceback from running this

Traceback (most recent call last):
  File "testls.py", line 8, in <module>
    print(s.encode('UTF-8'))
  File "/usr/local/Cellar/python@3.7/3.7.11/Frameworks/Python.framework/Versions/3.7/lib/python3.7/collections/__init__.py", line 1163, in __str__
    def __str__(self): return str(self.data)
  File "/Users/brian/.virtualenvs/nips3/lib/python3.7/site-packages/lazy_string.py", line 56, in data
    return self._func(*self._args, **self._kwargs)
TypeError: 'bytes' object is not callable

Here is a patch to fix this problem. If you give me permission to the repo, I'll try to create a pull request.

Index: lazy_string.py
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/lazy_string.py b/lazy_string.py
--- a/lazy_string.py	(revision c35803cd389e862a5f1c8671b49aff18cf7b75e5)
+++ b/lazy_string.py	(date 1633030832060)
@@ -36,12 +36,13 @@
   __slots__ = ("_func", "_args", )
 
   def __new__(cls, func: Union[Callable, str], *args: Tuple, **kwargs: Mapping) -> object:
-    if isinstance(func, str):
+    if isinstance(func, str) or isinstance(func, bytes):
       # Many UserString's functions like `lower`, `__add__` and so on wrap
       # returned values with a call to `self.__class__(...)` to ensure the
       # result is of the same type as the original class.
       # However, as the result of all of such methods is always a string,
       # there's no need to create a new instance of a `LazyString`
+      # Same goes for function `encode` which returns a bytes object
       return func
 
     return object.__new__(cls)

I agree that there is a bug, though I don't see how the sample code hits it.

I also don't agree that the suggestion is the correct remediation.

I would propose that we check whether func is Callable and if it is not, then do something. We can still check if it is str (or UserString) and return them untouched, but if func is none of these three types, then we should probably convert func to a str and return that value.

I don't recall the details and I'm not longer able to reproduce the issue. My fuzzy memory was that it seemed like something was trying to return the data somewhere as a new LazyString but that doesn't appear to be the case so my memory must be suspect.

Since I can't reproduce this I'm fine if you would like to close it. Apologies for taking your time.

Ah! I found what changed that fixes the problem. Python 3.7 attempts to return the result of UserString.encode as a LazyString, but this looks to be changed in Python 3.8! Since it's fixed in 3.8 and later I definitely think this can be closed.

Python 3.7 code: https://github.com/python/cpython/blob/dfc5e451b59e578b4e5eaddc34d4766271cb09dd/Lib/collections/__init__.py#L1232

Python 3.8 code:
https://github.com/python/cpython/blob/f78733b332966cef7d0c61165389549f4ffe0727/Lib/collections/__init__.py#L1213

Sincerely thank you for your efforts and sorry for not replying for a while! Closing the issue.