Skip to content

Avoid calling __init__ for classmethod/staticmethod #450

Open

Description

@saluto

Suppose we want a CLI that is similar to git in the sense that we must first call init before interacting with the repo. For this, we aim for the following (simplified/partially implemented) class, which is intentionally similar to GitPythons Repo class (see here):

from pathlib import Path
import fire

class SomethingLikeGit:

  dirname = ".something_like_git"

  @classmethod
  def init(cls, path=None):
    path = Path(path or Path.cwd())
    path = path / cls.dirname
    print("Initializing at", path)
    path.mkdir()
    ...
    print("Done.")

  def __init__(self, path=None):
    path = Path(path or Path.cwd())
    for parent in (path, *path.parents):
      self.path = parent / self.dirname
      if self.path.exists():
        break
    else:
      raise FileNotFoundError(path)

  ...

if __name__ == "__main__":
  fire.Fire(SomethingLikeGit)

When we run the command python something_like_git.py init, it first tries to instantiate the class via __init__, which fails (FileNotFoundError) since it must first create the directory via init.
I think it makes generally more sense if Fire would call classmethods/staticmethods directly on the class without trying to instantiate it. This is usually also how class/static methods are used in a programmatic way (e.g. for git.Repo).

Note that the fix for #113 does not resolve this issue. This is because the path argument for __init__ is (intentionally) optional, so Fire thinks it can call __init__ first. If path wasn't optional, it would behave as wanted: init would be called without first calling __init__.

Anyway, thanks for your great work!

Activity

  1. RAGHAV2405AR commented on Mar 16, 2026

    @RAGHAV2405AR

    Hi, I was able to reproduce the behavior described in this issue.

    Reproduction

    Using the example code

    Image

    Running:

    python fire_repo_init_bug.py init
    

    produces:

    FileNotFoundError
    

    This happens because Fire attempts to instantiate the class before calling init(). Since __init__(path=None) has an optional argument, Fire assumes it can safely construct the class first.

    Workaround

    A simple workaround is to expose CLI commands instead of passing the class directly to fire.Fire().

    Example:

    class CLI:
    
        def init(self, path=None):
            return SomethingLikeGit.init(path)
    
        def repo(self, path=None):
            return SomethingLikeGit(path)
    
    if __name__ == "__main__":
        fire.Fire(CLI)

    With this approach, running:

    python fire_repo_init_bug.py init
    

    correctly produces:

    Initializing at .../.something_like_git
    Done.
    

    This prevents Fire from calling __init__ before init().

    Let me know if you'd like me to open a PR adding this workaround as an example in the documentation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      ⌂ Proxy