Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

unhashable type: 'types.SimpleNamespace' when using uninitialized logging or entering alive_bar's context twice #107

Closed
dbrocha opened this issue Sep 2, 2021 · 21 comments

Comments

@dbrocha
Copy link

dbrocha commented Sep 2, 2021

The "incr" param is gone in v2.0 and I believe this arg is occasionally being misassigned.
Error:
Exception has occurred: TypeError
unhashable type: 'types.SimpleNamespace'

@rsalmei
Copy link
Owner

rsalmei commented Sep 2, 2021

You don't need incr keyword argument anymore. Just send the number to increment.

@rsalmei
Copy link
Owner

rsalmei commented Sep 2, 2021

But I don't understand the error. What are you calling? And what is the complete stacktrace?

@dbrocha
Copy link
Author

dbrocha commented Sep 3, 2021

Thanks for the reply -- In testing the new version, I saw the incr parameter was no longer defined, so I removed it from my code. After doing that, I'm occasionally getting the error below when calling bar with an integer to increment.

The code erroring out now is the "bar(chunkSize)" call below:

with alive_bar(int(sourceRowCount), bar='circles', spinner=randSpinner(), enrich_print=False) as bar:
        i = 0
        for chunk in pd.io.sql.read_sql(query,sourceEngine,chunksize=chunkSize,coerce_float=False):
            mode = "w" if i == 0 else "a"
            header = True if i == 0 else False
            chunk.to_csv(targetFilePath, encoding='utf-8', index=False, mode=mode, header=header, sep=sep, quoting=csv.QUOTE_MINIMAL, compression=comp)
            chunkSize = int(len(chunk))
            bar(chunkSize)
            i += 1

Full error stack:

<●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●○>    ➜➜ 140000/143902 [97%] in 19s (7325.4/s, eta: 2s)Traceback (most recent call last):
  File "c:/Users/user/source/repos/---------/---------/---------.py", line 424, in <module>
    qa()
  File "c:/Users/user/source/repos/---------/---------/---------.py", line 419, in qa
    sql2hadoop('ghspacdc2w12', 'FOO', 'dbr', 'testtesttest', 'research_dev', 'dbr_BEST_TEST')
  File "c:/Users/user/source/repos/---------/---------/---------.py", line 319, in sql2hadoop
    chunkSize = chunkSize
  File "c:/Users/user/source/repos/---------/---------/---------.py", line 121, in sql2csv
    sep = sep
  File "c:/Users/user/source/repos/---------/---------/---------.py", line 92, in database2File
    i += 1
  File "C:\Program Files\Python37\lib\contextlib.py", line 119, in __exit__
    next(self.gen)
  File "C:\Users\user\source\repos\---------\.venv\lib\site-packages\alive_progress\core\progress.py", line 251, in __alive_bar
    stop_monitoring()
  File "C:\Users\user\source\repos\---------\.venv\lib\site-packages\alive_progress\core\progress.py", line 151, in stop_monitoring
    hook_manager.uninstall()
  File "C:\Users\user\source\repos\---------\.venv\lib\site-packages\alive_progress\core\hook_manager.py", line 75, in uninstall
    for handler, original_stream in before_handlers.items()]
  File "C:\Users\user\source\repos\---------\.venv\lib\site-packages\alive_progress\core\hook_manager.py", line 75, in <listcomp>
<●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●> ➞   ➜ 143902/143902 [100%] in 19s (7515.1/s, eta: 1s)    for handler, original_stream in before_handlers.items()]
  File "C:\Users\user\source\repos\---------\.venv\lib\site-packages\alive_progress\core\hook_manager.py", line 96, in _set_stream
    return handler.setStream(stream)
  File "C:\Program Files\Python37\lib\logging\__init__.py", line 1049, in setStream
    self.flush()
  File "C:\Program Files\Python37\lib\logging\__init__.py", line 1009, in flush
    self.stream.flush()
  File "C:\Users\user\source\repos\---------\.venv\lib\site-packages\alive_progress\core\hook_manager.py", line 31, in flush
    if buffers[stream]:
TypeError: unhashable type: 'types.SimpleNamespace'
Error in atexit._run_exitfuncs:
Traceback (most recent call last):
  File "C:\Program Files\Python37\lib\logging\__init__.py", line 2033, in shutdown
    h.flush()
  File "C:\Program Files\Python37\lib\logging\__init__.py", line 1009, in flush
    self.stream.flush()
  File "C:\Users\user\source\repos\---------\.venv\lib\site-packages\alive_progress\core\hook_manager.py", line 31, in flush
    if buffers[stream]:
TypeError: unhashable type: 'types.SimpleNamespace'

@rsalmei
Copy link
Owner

rsalmei commented Sep 3, 2021

Great! Now I can see what did happen.
It seems like a real bug, I'll try to simulate it here, and fix asap 👍

@rsalmei
Copy link
Owner

rsalmei commented Sep 3, 2021

You use logging in this program, right?
It seems it was set up twice somehow, like one alive_bar inside of another...
Please show me how you set up your logging environment, will you?

@dbrocha
Copy link
Author

dbrocha commented Sep 3, 2021

There isn't logging in this call and no nested progress bars either. It's using a sqlalchemy engine created here via pyodbc for the sourceEngine:

def sqlEngine(
        serverName,
        databaseName,
        fast_executemany = True
    ):
    connectionString = f"mssql+pyodbc://{serverName}/{databaseName}?driver=SQL+Server+Native+Client+11.0"
    return create_engine(connectionString, fast_executemany = fast_executemany)

Other than that engine being opened by pd.read_sql, the with alive_bar... code above is the full loop.

@Nemexur
Copy link

Nemexur commented Sep 8, 2021

I have the same problem with rich.console.
I'm using alive_it and do not have nested progress bars for sure.
Traceback:

│ ------------------------------------------------------------------------------------ log_metrics |                                                                           │
│                                                                                                  │
│   168 │   │   if flush:                                                                          
│   169 │   │   │   self._delete_last_lines(len(metrics))                                          │
│   170 │   │   else:                                                                              
│ ❱ 171 │   │   │   self._console.print("---------------------", style="bold magenta")             │
│   172 │   │   for metric in metrics:                                                             
│   173 │   │   │   self._console.print(metric)                                                    │
│                                                                                                  │
│ ╭──────────────────────────────────── locals ─────────────────────────────────────╮              │
│ │   flush = False                                                                 │              │
│ │ metrics = [                                                                     |              |                    
| |             ------------------------------------                                │              │
│ │           ]                                                                     │              │
│ │    self = <------------------.training.utils.ProgressBar object at 0x13edccfd0> │              │
│ ╰─────────────────────────────────────────────────────────────────────────────────╯              │
│                                                                                                  │
│ -------------------------------------------------------------/.venv/lib/python3.7/site-packages/ │
│ rich/console.py:1615 in print                                                                    │
│                                                                                                  │
│   1612 │   │   │   │   ):                                                                        
│   1613 │   │   │   │   │   buffer_extend(line)                                                   │
│   1614 │   │   │   else:                                                                         
│ ❱ 1615 │   │   │   │   self._buffer.extend(new_segments)                                         │
│   1616 │                                                                                         │
│   1617 │   def print_json(                                                                       │
│   1618 │   │   self,                                                                             │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │  buffer_extend = <built-in method extend of list object at 0x13edd0948>                      │ │
│ │           crop = True                                                                        │ │
│ │          emoji = None                                                                        │ │
│ │            end = '\n'                                                                        │ │
│ │         extend = <built-in method extend of list object at 0x13edfa408>                      │ │
│ │         height = None                                                                        │ │
│ │      highlight = None                                                                        │ │
│ │        justify = None                                                                        │ │
│ │           line = [                                                                           │ │
│ │                  │   Segment(                                                                │ │
│ │                  │   │   '---------------------',                                            │ │
│ │                  │   │   Style(                                                              │ │
│ │                  │   │   │   color=Color('magenta', ColorType.STANDARD, number=5),           │ │
│ │                  │   │   │   bold=True                                                       │ │
│ │                  │   │   )                                                                   │ │
│ │                  │   ),                                                                      │ │
│ │                  │   Segment('\n',)                                                          │ │
│ │                  ]                                                                           │ │
│ │         markup = None                                                                        │ │
│ │ new_line_start = False                                                                       │ │
│ │   new_segments = [                                                                           │ │
│ │                  │   Segment(                                                                │ │
│ │                  │   │   '---------------------',                                            │ │
│ │                  │   │   Style(                                                              │ │
│ │                  │   │   │   color=Color('magenta', ColorType.STANDARD, number=5),           │ │
│ │                  │   │   │   bold=True                                                       │ │
│ │                  │   │   )                                                                   │ │
│ │                  │   ),                                                                      │ │
│ │                  │   Segment(                                                                │ │
│ │                  │   │   '\n',                                                               │ │
│ │                  │   │   Style(                                                              │ │
│ │                  │   │   │   color=Color('magenta', ColorType.STANDARD, number=5),           │ │
│ │                  │   │   │   bold=True                                                       │ │
│ │                  │   │   )                                                                   │ │
│ │                  │   )                                                                       │ │
│ │                  ]                                                                           │ │
│ │        no_wrap = None                                                                        │ │
│ │        objects = ('---------------------',)                                                  │ │
│ │       overflow = None                                                                        │ │
│ │         render = <bound method Console.render of <console width=176 ColorSystem.TRUECOLOR>>  │ │
│ │ render_options = ConsoleOptions(size=ConsoleDimensions(width=176, height=43),                │ │
│ │                  legacy_windows=False, min_width=1, max_width=176, is_terminal=True,         │ │
│ │                  encoding='utf-8', max_height=43, justify=None, overflow=None, no_wrap=None, │ │
│ │                  highlight=None, markup=None, height=None)                                   │ │
│ │     renderable = <text '---------------------' []>                                           │ │
│ │    renderables = [<text '---------------------' []>]                                         │ │
│ │           self = <console width=176 ColorSystem.TRUECOLOR>                                   │ │
│ │            sep = ' '                                                                         │ │
│ │      soft_wrap = False                                                                       │ │
│ │          style = 'bold magenta'                                                              │ │
│ │          width = None                                                                        │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ -------------------------------------------------------------/.venv/lib/python3.7/site-packages/ │
│ rich/console.py:825 in __exit__                                                                  │
│                                                                                                  │
│    822 │                                                                                         │
│    823 │   def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:            │
│    824 │   │   """Exit buffer context."""                                                        │
│ ❱  825 │   │   self._exit_buffer()                                                               │
│    826 │                                                                                         │
│    827 │   def begin_capture(self) -> None:                                                      
│    828 │   │   """Begin capturing console output. Call :meth:`end_capture` to exit capture mod   │
│                                                                                                  │
│ ╭─────────────────────── locals ────────────────────────╮                                        │
│ │  exc_type = None                                      │                                        │
│ │ exc_value = None                                      │                                        │
│ │      self = <console width=176 ColorSystem.TRUECOLOR> │                                        │
│ │ traceback = None                                      │                                        │
│ ╰───────────────────────────────────────────────────────╯                                        │
│                                                                                                  │
│ -------------------------------------------------------------/.venv/lib/python3.7/site-packages/ │
│ rich/console.py:784 in _exit_buffer                                                              │
│                                                                                                  │
│    781 │   def _exit_buffer(self) -> None:                                                       
│    782 │   │   """Leave buffer context, and render content if required."""                       │
│    783 │   │   self._buffer_index -= 1                                                           │
│ ❱  784 │   │   self._check_buffer()                                                              │
│    785 │                                                                                         │
│    786 │   def set_live(self, live: "Live") -> None:                                             │
│    787 │   │   """Set Live instance. Used by Live context manager.                               │
│                                                                                                  │
│ ╭───────────────────── locals ─────────────────────╮                                             │
│ │ self = <console width=176 ColorSystem.TRUECOLOR> │                                             │
│ ╰──────────────────────────────────────────────────╯                                             │
│                                                                                                  │
│ -------------------------------------------------------------/.venv/lib/python3.7/site-packages/ │
│ rich/console.py:1866 in _check_buffer                                                            │
│                                                                                                  │
│   1863 │   │   │   │   │   │   │   │   for line in text.splitlines(True):                        
│   1864 │   │   │   │   │   │   │   │   │   write(line)                                           │
│   1865 │   │   │   │   │   │   │   else:                                                         
│ ❱ 1866 │   │   │   │   │   │   │   │   self.file.write(text)                                     │
│   1867 │   │   │   │   │   │   │   self.file.flush()                                             │
│   1868 │   │   │   │   │   │   except UnicodeEncodeError as error:                               
│   1869 │   │   │   │   │   │   │   error.reason = f"{error.reason}\n*** You may need to add PY   │
│                                                                                                  │
│ ╭───────────────────── locals ──────────────────────╮                                            │
│ │ self = <console width=176 ColorSystem.TRUECOLOR>  │                                            │
│ │ text = '\x1b[1;35m---------------------\x1b[0m\n' │                                            │
│ ╰───────────────────────────────────────────────────╯                                            │
│                                                                                                  │
│ -------------------------------------------------------------/.venv/lib/python3.7/site-packages/ │
│ alive_progress/core/hook_manager.py:36 in write                                                  │
│                                                                                                  │
│    33 │   │   │   stream.flush()                                                                 │
│    34 │                                                                                          │
│    35 │   def write(stream, part):                                                               
│ ❱  36 │   │   buffer = buffers[stream]                                                           │
│    37 │   │   if part != '\n':                                                                   
│    38 │   │   │   # this will generate a sequence of lines interspersed with None, which will    │
│    39 │   │   │   # be rendered as the indent filler to align additional lines under the same    │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │         base = (                                                                             │ │
│ │                │   namespace(flush=functools.partial(<function                               │ │
│ │                buffered_hook_manager.<locals>.flush at 0x13eb462f0>, <_io.TextIOWrapper      │ │
│ │                name='<stdout>' mode='w' encoding='UTF-8'>), isatty=<built-in method isatty   │ │
│ │                of _io.TextIOWrapper object at 0x108db2630>,                                  │ │
│ │                write=functools.partial(<function buffered_hook_manager.<locals>.write at     │ │
│ │                0x13eb46378>, <_io.TextIOWrapper name='<stdout>' mode='w'                     │ │
│ │                encoding='UTF-8'>)),                                                          │ │
│ │                │   namespace(flush=functools.partial(<function                               │ │
│ │                buffered_hook_manager.<locals>.flush at 0x13eb462f0>, <_io.TextIOWrapper      │ │
│ │                name='<stderr>' mode='w' encoding='UTF-8'>), isatty=<built-in method isatty   │ │
│ │                of _io.TextIOWrapper object at 0x108db2630>,                                  │ │
│ │                write=functools.partial(<function buffered_hook_manager.<locals>.write at     │ │
│ │                0x13eb46378>, <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>)) │ │
│ │                )                                                                             │ │
│ │      buffers = defaultdict(<class 'list'>, {})                                               │ │
│ │ cond_refresh = <Condition(<unlocked _thread.RLock object owner=0 count=0 at 0x13eb613f0>,    │ │
│ │                0)>                                                                           │ │
│ │   get_header = <function buffered_hook_manager.<locals>.<lambda> at 0x13edf79d8>             │ │
│ │         part = '\x1b[1;35m---------------------\x1b[0m\n'                                    │ │
│ │       stream = namespace(flush=functools.partial(<function                                   │ │
│ │                buffered_hook_manager.<locals>.flush at 0x13eb462f0>, <_io.TextIOWrapper      │ │
│ │                name='<stdout>' mode='w' encoding='UTF-8'>), isatty=<built-in method isatty   │ │
│ │                of _io.TextIOWrapper object at 0x108db2630>,                                  │ │
│ │                write=functools.partial(<function buffered_hook_manager.<locals>.write at     │ │
│ │                0x13eb46378>, <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>)) │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
TypeError: unhashable type: 'types.SimpleNamespace'

During handling of the above exception, another exception occurred:

@rsalmei
Copy link
Owner

rsalmei commented Sep 8, 2021

I can't simulate this at all.
Could anyone come up with a small example program that does trigger this behavior?

@Nemexur
Copy link

Nemexur commented Sep 8, 2021

I found that this problem is connected to overlapping context managers.
It happens in this case:

import time
from alive_progress import *


range_1 = alive_it(range(13))
with alive_bar(14) as bar_1:
    for item in range(13):
        print("hello")
        time.sleep(0.1)
        bar_1()
    with alive_bar(14) as bar_2:
        for item in range(10):
            print("hello")
            time.sleep(0.1)
            bar_2()

Clearly, it is not a proper way of using alive-progress. However, it occurs with ignite library for training neural networks. I guess for evaluating stage it can not properly finish context manager for the training progress bar. Is there a way to finish alive_it manually? bar.pause() did not help me((
Here is a small script to reproduce it with ignite:

from typing import Iterable, Any
import time
from loguru import logger
from rich.console import Console
from alive_progress import alive_it
from ignite.engine import Engine, Events


class ProgressBar:
    def __init__(self, iterable: Iterable, total: int = None, title: str = None, **options) -> None:
        self._console = Console()
        self._total = total or 0
        self._iterable = alive_it(
            iterable, total=self._total, title=title, enrich_print=False, receipt_text=True, **options
        )

    def __len__(self) -> int:
        return self._total

    def __iter__(self) -> Any:
        yield from self._iterable

    def text(self, desc: str) -> None:
        self._iterable.text(desc)

    def desc(self, desc: str) -> None:
        self._console.print(desc)


class SimpleIterable:
    def __init__(self, length: int) -> None:
        self._iterable = range(length)

    def __iter__(self) -> int:
        for item in self._iterable:
            yield item


trainer = Engine(lambda engine, sample: time.sleep(0.2))
evaluator = Engine(lambda engine, sample: time.sleep(0.2))
data_loaders = {"train": SimpleIterable(20), "evaluate": SimpleIterable(13)}


@trainer.on(Events.ITERATION_COMPLETED)
def on_train_batch(engine: Engine) -> None:
    engine.state.dataloader.desc("train batch processed")


@evaluator.on(Events.ITERATION_COMPLETED)
def on_eval_batch(engine: Engine) -> None:
    engine.state.dataloader.desc("eval batch processed")


@trainer.on(Events.EPOCH_STARTED)
def on_train_epoch_start(engine: Engine) -> None:
    engine.set_data(ProgressBar(data_loaders["train"], total=13, title="Train epoch"))


@evaluator.on(Events.EPOCH_STARTED)
def on_eval_epoch_start(engine: Engine) -> None:
    engine.set_data(ProgressBar(data_loaders["evaluate"], total=10, title="Evaluate epoch"))


@trainer.on(Events.EPOCH_COMPLETED)
def on_train_epoch_end(engine: Engine) -> None:
    logger.info("Train step completed")
    evaluator.run(data_loaders["evaluate"])


@evaluator.on(Events.EPOCH_COMPLETED)
def on_eval_epoch_end(engine: Engine) -> None:
    logger.info("Evaluate step completed")


if __name__ == "__main__":
    trainer.run(data_loaders["train"], max_epochs=3, epoch_length=7)

@rsalmei
Copy link
Owner

rsalmei commented Sep 9, 2021

It seems it was set up twice somehow, like one alive_bar inside of another...

So, it really was what I thought...
In this case, it can't be considered a bug, because it isn't how it is supposed to be used.
I've wanted to actually support multiple bars for a long time, but the real time speed adjusting of alive_progress makes it kinda difficult.
Anyway, I think I could at least avoid the error in this case, assuming the bars won't be used at the same time, but I can't do it at this time...

@dbrocha
Copy link
Author

dbrocha commented Sep 10, 2021

I definitely don't have nested bars in the case of my code above. I'll try to get some generic reproduceable code together for you.

@dbrocha
Copy link
Author

dbrocha commented Sep 10, 2021

Got it! Finally figured out the source here. There is a subprocess that gets called in an "unknown length" bar that uses logging. The defined length bar that follows errors out. Check this out:

import time
import logging
from alive_progress import alive_bar

def subprocessWithLogging():
    logging.info('whoops')

def unknownTest():
    with alive_bar(enrich_print=False) as bar:
        print('This is an unknown length bar')
        time.sleep(1)
        bar()
        print('This is another step with logging')
        subprocessWithLogging()
        time.sleep(1)
        bar()

def definiteTest():
    with alive_bar(100, bar='circles', enrich_print=False) as bar:
        for _ in range(100):
            time.sleep(.01)
            bar()

def main():
    "This runs successfully"
    definiteTest()

    unknownTest()
    "This does not"
    definiteTest()

main()

@Nemexur
Copy link

Nemexur commented Sep 10, 2021

I guess you need to instantiate a logger instance with a specified name to fix it.

logger = logging.getLogger("alive-progress")


def subprocessWithLogging():
    logger.info("whoops")

I can not reproduce it with these settings.

@rsalmei
Copy link
Owner

rsalmei commented Sep 12, 2021

Thank you @dbrocha, I see now what's happening...
It's only a setup problem, like @Nemexur had anticipated (thanks!).

See, at the time you enter alive_bar's context manager, I fetch and instrument all existing logging stream handlers. In this process, I exchange the real stream for another one which buffers, enriches and cleans the line when anything is logged, exactly like the stderr and stdout print hooks.

But since you are not calling .info on a logger, it's actually the free function info within the logging module, the environment is not set up yet... Look at its code:

def info(msg, *args, **kwargs):
    if len(root.handlers) == 0:
        basicConfig()  <<<<<<<<<<<<<<<
    root.info(msg, *args, **kwargs)

That basicConfig() call sets up the logging in a bad moment, within a running alive_bar context....... This means the stderr and stdout were already instrumented, and thus were copied into this default handler. When this context ends it is not undone, since the bar does not know it even exists.

Fortunately the fix is really simple, just call logging.basicConfig() before entering alive_bar and you're good to go!
This works:

def main():
    "This runs successfully"
    definiteTest()

    unknownTest()
    "This does not"
    definiteTest()

logging.basicConfig()
main()

@rsalmei
Copy link
Owner

rsalmei commented Sep 12, 2021

Hey @Nemexur, regarding your "totally different problem but with the same outcome", it seems another beast altogether...

I don't know anything about pytorch or ignite, but I know that alive_it is a class with a __iter__() method, that delivers a generator when called.
So, the only ways I'm aware of to finish a generator (and thus alive_it) are exhausting it or triggering an exception.
I've initially tried a lot to trigger an exception on demand, via the .send generator method, but I couldn't, it's not straightforward at all.
So I tried to exhaust the generator instead, and that did work! Although I'm not sure why, I'll explain...

My first approach was to retrieve the generator out of the iterable class, and aggregate them in a tuple:

@trainer.on(Events.EPOCH_STARTED)
def on_train_epoch_start(engine: Engine) -> None:
    pb = ProgressBar(data_loaders["train"], total=13, title="Train epoch")
    engine.set_data((iter(pb), pb))

Then both use it:

@trainer.on(Events.ITERATION_COMPLETED)
def on_train_batch(engine: Engine) -> None:
    engine.state.dataloader[1].desc("train batch processed")

and exhaust its generator:

@trainer.on(Events.EPOCH_COMPLETED)
def on_train_epoch_end(engine: Engine) -> None:
    logger.info("Train step completed")
    for _ in engine.state.dataloader[0]:
        pass
    evaluator.run(data_loaders["evaluate"])

OK, IT WORKS!
asd

But then I tried to simplify it, and realized that even when I do not exhaust the generator it still works just the same!
Worse, if I change the trainer iteration completed event to not use the .desc method (put a simple print instead), I can just send a 1-element tuple on the start event and it still works.......

    engine.set_data((iter(pb),))

EVEN worse, if I remove the tuple, change to just the generator, it breaks again 😕

    engine.set_data(iter(pb))

That's it, I'm not sure what's happening... Can you make sense of this?

@rsalmei rsalmei changed the title unhashable type: 'types.SimpleNamespace' when calling bar(i) with int iterator i unhashable type: 'types.SimpleNamespace' when using uninitialized logging or entering alive_bar's context twice Sep 12, 2021
@Nemexur
Copy link

Nemexur commented Sep 12, 2021

First of all, thanks for the help @rsalmei!!
For better understanding i need to outline how ignite process's data loaders.

def set_data(self, data_loader):
    self.state.dataloader = data_loader
    self._dataloader_iter = iter(data_loader)

def run(self, ...):
    iter_counter = 0
    # ...
    while True:
        try:
            # ...
            self.state.batch = next(self._dataloader_iter)
            # ...
            iter_counter += 1
            should_exit = False
        except StopIteration:
            if should_exit:
                # ...
                break
            self.set_data(self.state.dataloader)
            should_exit = True
            continue
        # Process batch
        if self.state.epoch_length is not None and self.state.epoch_length == iter_counter:
            break
    ...

Here is the thing, alive_it can not be exhausted if epoch_length is present, like in the above case. So the only way to close it is to empty the generator manually, as you mentioned. It seems like a good solution. However, getting new batches for the training phase could be time-consuming (and it happens for big datasets with lots of samples). That's why I do not consider it a good solution. For tqdm progress bar, I found a work-around by calling close.

Regarding your solution with engine.set_data((iter(data), )). It manages to hack process function passed during Engine initialization. In the run method, it always raises StopIteration error on the second sample.

Thus, the problem occurs when we try to start a new alive_it without properly finishing (or exhausting) the previous one. At the moment, close seems like the best solution. Also, it would greatly help when you can not call with bar.pause(): yield.... However, only time would tell if it's a good feature for other users))

@rsalmei
Copy link
Owner

rsalmei commented Sep 12, 2021

You're welcome @Nemexur!
Hey, turns out close() like you mentioned is a method on generators! It really works to stop it prematurely!
Here it is: https://docs.python.org/3/reference/expressions.html#generator.close

I've tried to remove the tuple hack and just sent:

engine.set_data(iter(pb))

Then, on train_epoch_end, we can just:

engine.state.dataloader.close()

Simple and clean humm? 👍

@Nemexur
Copy link

Nemexur commented Sep 12, 2021

Indeed, very simple and clean. I did not know that about python generators. Thanks!!

@rsalmei
Copy link
Owner

rsalmei commented Sep 12, 2021

Yeah, there's even a throw method I didn't recall, it is easy to command a generator to throw an exception!
You're welcome man.
I'm going to close this issue then, since it appears both problems were addressed. See ya!

@rsalmei rsalmei closed this as completed Sep 12, 2021
@dbrocha
Copy link
Author

dbrocha commented Sep 13, 2021

Thanks @rsalmei and @Nemexur! Appreciate the help debugging and the simple fix.

@Miladiouss
Copy link

I encountered the same error not when running nested bars directly but when PyTest runs nested bars. The solution is simple: don't use nested bars. But I leave this here for completeness and in case others come across this issue through PyTest.

# installed using: pip install alive-progress
# alive-progress version/build/channel: 2.1.0 pypi_0 pypi
# To recreate the error, run this script using pytest command.
from alive_progress import alive_bar
from time import sleep
def bar_1():
    items = list('abcdefg')
    with alive_bar(len(items), title='Bar-1: ') as bar:
        for i, idx in enumerate(items):
            sleep(.5)
            bar()
def test_nested_bar():
    items = list('123456')
    with alive_bar(len(items), title='Bar-2: ') as bar:
        for i, idx in enumerate(items):
            bar_1()
            sleep(.5)
            bar()

Last error message:

TypeError: unhashable type: 'types.SimpleNamespace'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants