Coder Social home page Coder Social logo

madisonaster / foofinder Goto Github PK

View Code? Open in Web Editor NEW
2.0 1.0 0.0 206 KB

A package designed to help you find foo. FooFinder makes relative imports easy with this one simple trick!

License: GNU Lesser General Public License v2.1

Python 100.00%
pypi python extensions relative imports importer

foofinder's Introduction

PyPI PyPI - Python Version Build Status Azure Status

A package designed to help you find foo. FooFinder makes relative imports easy with this one simple trick!


Installation:

pip install FooFinder

Usage:

ParentFolder/
    MySuperAwesomeModule.py                 <- and you want to import this awesome module
    SubFolder1/
        SubFolder2/
            CurrentModule.py           <- you are here
from FooFinder import MySuperAwesomeModule

FooFinder will walk up to each parent directory, and down to each child directory, from the location of your current module. FooFinder will import and return the first module or package that it finds with the name you give it.


Other ways to use FooFinder:

Using a namespace:

from FooFinder import CoolStuff as SuperCoolStuff

Finding a downstream module:

ParentFolder/
    CurrentModule.py                   <- you are here
    SubFolder1/
        SubFolder2/
            DownstreamModule.py             <- and you want to import this downstream module
from FooFinder import DownstreamModule

Importing a package:

ParentPackage/
    __init__.py                                <- and you want to import this parent package
    SubFolder1/
	NeighboringPackage/	               <- and this neighboring package
	    __init__.py
        SubFolder2/
	    SubFolder3/
		DownstreamPackage/	       <- and this downstream package
		    __init__.py
            CurrentModule.py           <- you are here
from FooFinder import ParentPackage
from FooFinder import NeighboringPackage
from FooFinder import DownstreamPackage

FooFinder can find the package you're currently in, a package from a parent directory, or a package from a child directory. Modules within packages will be found as if they were folders.


Using a parent package as a reference point:

ParentFolder/
    __init__.py
    SubFolder1/
        TargetModule.py                     <- and you want to import this module
    SubFolder2/
        SubSubFolder/
            CurrentModule.py           <- you are here
from FooFinder.ParentFolder import TargetModule

Using a neighboring package as a reference point:

ParentFolder/
    SubFolder1/
        __init__.py
        SubSubFolder/
            TargetModule.py                 <- and you want to import that
    SubFolder2/
        SubSubFolder/
            CurrentNotebook.ipynb      <- you are here
from FooFinder.SubFolder1 import TargetModule
from FooFinder.SubFolder1 import SomePackageAttribute

Using a root module as a reference point:

ParentFolder/
    RootModule.py
    SubFolder1/
        SubSubFolder/
            TargetPackage/                  <- and you want to import this package
                __init__.py
    SubFolder2/
        SubSubFolder/
            CurrentModule.py           <- you are here
from FooFinder.RootModule import TargetPackage

Importing Modules and Packages from a zip file:

ParentFolder/
    ZippedFile.zip/
        ZippedPackage/                      <- and you want to import this zipped package
            __init__.py
        ZippedModule.py                     <- or this zipped module
    SubFolder1/
        SubFolder2/
            CurrentModule.py         <- you are here
from FooFinder.ZippedFile import ZippedPackage
from FooFinder.ZippedFile import ZippedModule

Limitations:

FooFinder will not search directories that start with a "." such as .git or .hg, it will also skip any egg-info or __pycache__ folders. FooFinder will try each possible parent path to your module with os.path.exists instead of retrieving a directory listing. It will not recurse the parent folders. If none of the parent paths exist it will then recursively try each path downwards from your current module. If you're using a reference point, it will first find the reference point, and then search only downwards from the reference point.

Zip importing uses zipimport.zipimporter.load_module so it's limited to just what zipimporter can see, and can't recursively search the contents of the zip file.

If a user has modules or packages installed at the root of their system that have the same name you're trying to import, there could be a conflict. However this would be a very strange system configuration to have, so the risk of this should be quite minimal.


See the test module for example usage and complete test coverage.

foofinder's People

Contributors

madisonaster avatar

Stargazers

 avatar  avatar

Watchers

 avatar

foofinder's Issues

Does anyone have an idea for cleaning up this hack?

#replace builtin importer so the ugly hack below only has to run once
original_import = builtins.__import__
builtins.__import__ = _import
##this hack is lame...
frame = inspect.currentframe()
while inspect.getframeinfo(frame).function != '_find_and_load':
frame = frame.f_back
frame = frame.f_back #go 1 more step back to get calling function
context = inspect.getframeinfo(frame).code_context[0] #get line that called FooFinder
mname = context.split('from FooFinder import ',1)[-1].split(' ')[0].split('#')[0] #split on syntax
if mname != '': #import FooFinder shouldn't run _import
args = ('','',(mname,))
_import('FooFinder', *args, frame=frame)

I can't override builtins.__import__ the first time it's ran, so the issue is with trying to extract the name of the module that the user is calling. When builtins.__import__ calls the module, it's already consumed the args and doesn't pass them into the module, which makes sense because who passes args to a module? But I've gone through all the frames, and I can't seem to find a way to get the original pointer to *args, where from mymodulename parsed the module name.

In order to get this to work, I walked back to the code context and simply hacked it by parsing the statement as you can see from:

context = inspect.getframeinfo(frame).code_context[0] #get line that called FooFinder
mname = context.split('from FooFinder import ',1)[-1].split(' ')[0].split('#')[0] #split on syntax

Does anyone know how to get the pointer to __import__'s args?

What I have works fine, and stable across all versions of python 3, I just don't think it's proper to be re-parsing the code.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.