devbisme / skidl

SKiDL is a module that extends Python with the ability to design electronic circuits.
https://devbisme.github.io/skidl/
MIT License
1.06k stars 119 forks source link

[SKiDL BUG] unable to use StringIO with generate_netlist #101

Open adamws opened 3 years ago

adamws commented 3 years ago

Describe the bug According to this description:

generate_netlist(**kwargs) method of skidl.Circuit.Circuit instance
    Return a netlist and also write it to a file/stream.

    Args:
        file_: Either a file object that can be written to, or a string
            containing a file name, or None.

I think that it should be possible to use StringIO, instead I'm getting: AttributeError: '_io.StringIO' object has no attribute 'mode'

To Reproduce

aws:skidl/ (master) $ python
Python 3.9.1 (default, Dec 13 2020, 11:55:53) 
[GCC 10.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from skidl import *
WARNING: KICAD_SYMBOL_DIR environment variable is missing, so the default KiCad symbol libraries won't be searched.
>>> import sys
>>> generate_netlist(file_=sys.stdout)

No errors or warnings found during netlist generation.

(export (version D)
  (design
    (source "/home/aws/git/skidl/skidl/Circuit.py")
    (date "01/15/2021 10:54 PM")
    (tool "SKiDL (0.0.30)"))
  (components)
  (nets)
)
'(export (version D)\n  (design\n    (source "/home/aws/git/skidl/skidl/Circuit.py")\n    (date "01/15/2021 10:54 PM")\n    (tool "SKiDL (0.0.30)"))\n  (components)\n  (nets)\n)\n'
>>> import io
>>> output = io.StringIO()
>>> generate_netlist(file_=output)

No errors or warnings found during netlist generation.

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/aws/git/skidl/skidl/Circuit.py", line 445, in generate_netlist
    with opened(file_ or (get_script_name() + ".net"), "w") as f:
  File "/usr/lib/python3.9/contextlib.py", line 117, in __enter__
    return next(self.gen)
  File "/home/aws/git/skidl/skidl/utilities.py", line 732, in opened
    if mode.replace("+", "") == f_or_fn.mode.replace("+", ""):
AttributeError: '_io.StringIO' object has no attribute 'mode'

Expected behavior Using StringIO should be possible:

aws:skidl/ (master✗) $ python
Python 3.9.1 (default, Dec 13 2020, 11:55:53) 
[GCC 10.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from skidl import *
WARNING: KICAD_SYMBOL_DIR environment variable is missing, so the default KiCad symbol libraries won't be searched.
>>> import io
>>> output = io.StringIO()
>>> generate_netlist(file_=output)

No errors or warnings found during netlist generation.

'(export (version D)\n  (design\n    (source "/home/aws/git/skidl/skidl/Circuit.py")\n    (date "01/15/2021 10:58 PM")\n    (tool "SKiDL (0.0.30)"))\n  (components)\n  (nets)\n)\n'
>>> print(output.getvalue())
(export (version D)
  (design
    (source "/home/aws/git/skidl/skidl/Circuit.py")
    (date "01/15/2021 10:58 PM")
    (tool "SKiDL (0.0.30)"))
  (components)
  (nets)
)

Although this is probably not a solution I did something like this in order to get expected behavior presented above (and didn't analyze this any further):

diff --git a/skidl/utilities.py b/skidl/utilities.py
index f175ef3..083b3e3 100644
--- a/skidl/utilities.py
+++ b/skidl/utilities.py
@@ -729,13 +729,7 @@ def opened(f_or_fn, mode):
         with open(f_or_fn, mode, encoding="utf-8") as f:
             yield f
     elif hasattr(f_or_fn, "fileno"):
-        if mode.replace("+", "") == f_or_fn.mode.replace("+", ""):
-            # same mode, can reuse file handle
-            yield f_or_fn
-        else:
-            # open in new mode
-            with os.fdopen(f_or_fn.fileno(), mode) as f:
-                yield f
+        yield f_or_fn
     else:
         raise TypeError(
             "argument must be a filename or a file-like object (is: {})".format(

Desktop (please complete the following information):