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

AttributeError when calling a method which returns a BeautifulSoup object #245

Open
gab50000 opened this issue Apr 19, 2020 · 2 comments
Open

Comments

@gab50000
Copy link

@gab50000 gab50000 commented Apr 19, 2020

This is a small example to reproduce the error:

import bs4
import fire

assert fire.__version__ == "0.3.1"

class Test:
    def get_soup(self):
        soup = bs4.BeautifulSoup("<h1>hi</h1>")
        return soup


fire.Fire(Test)

If I run this file via

python test.py get_soup

I get the error "AttributeError: 'NoneType' object has no attribute 'get'".

If I run

python test.py get_soup -- --interactive

the variable result does not contain the expected soup object, but instead "<bound method Test.get_soup of <__main__.Test object at 0x7f80840e5460>>"

@WaizungTaam
Copy link

@WaizungTaam WaizungTaam commented Apr 25, 2020

The error log is

...\fire\core.py", line 775, in _ParseArgs
    accepts_positional_args = metadata.get(decorators.ACCEPTS_POSITIONAL_ARGS)
AttributeError: 'NoneType' object has no attribute 'get'

From https://github.com/google/python-fire/blob/master/fire/decorators.py#L87

def GetMetadata(fn):
  # Class __init__ functions and object __call__ functions require flag style
  # arguments. Other methods and functions may accept positional args.
  default = {
      ACCEPTS_POSITIONAL_ARGS: inspect.isroutine(fn),
  }
  return getattr(fn, FIRE_METADATA, default)

GetMetadata uses getattr to retrieve the fire metadata from component fn. In this case, fn is a bs4.BeautifulSoup.

For bs4.BeautifulSoup (bs4/__init__.py#L68), it inherits from class bs4.Tag, which overrides __getattr__ (bs4/element.py#L1433).

def __getattr__(self, tag):
        """Calling tag.subtag is the same as calling tag.find(name="subtag")"""
        ...

So getattr(fn, FIRE_METADATA, default) for a BeautifulSoup instance always return None for the non-existing subtag, and calling metadata.get later raises the above exception.

A quick fix is to check if the getattr result is a dict with key ACCEPTS_POSITIONAL_ARGS, and return default if it is not.

@dbieber
Copy link
Member

@dbieber dbieber commented Apr 26, 2020

Thanks for diving into that @WaizungTaam! Your suggested fix sounds good to me.

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

Successfully merging a pull request may close this issue.

None yet
3 participants
You can’t perform that action at this time.