/pathed

importing and pathing in python, made simpler

Primary LanguagePythonMIT LicenseMIT

Why pathed?

Want to import a file that can't be accessed by "import"? Or a whole directory of files? This helps with that! Also, common path operations like find('*.txt'), rmfile, mkfile, read, and more are in the Path module. Tired of writing str(Path())? Try the Path module. Inspired by pathlib, os.path, shutil, and glob.


One Liner for Relative Importing

Completely changes the way the "from _ import _" statement to use relative importing

import builtins, pathed; builtins.__import__ = pathed.new_import

Restore "from" statement

builtins.__import__ = pathed.old_import

Quick Start On Normal Modules

Relative Importing

import builtins, pathed
builtins.__import__ = pathed.new_import

from ....Destkop.new import *
# imports classes, functions, and variables from new.py
# new.py is up 3 directories, down 1 directory
# MUST use the word "from" in order to do relative importing

import directory.lambda
# this uses the normal import statement because 
# there's no "from" keyword in the beginning

builtins.__import__ = pathed.old_import
# use the "from" keyword normally after this

Importing files, folders, and adding to paths should be easy

from pathed import cwd, importfile, importdir

# import file or directory to variable
name = importfile(cwd/'..'/'..'/'name.py')

dirname = importdir(cwd/'..'/'..')

path = cwd/2/3+'.txt' # accepts objects that can be converted into string

cwd.find(*.txt) # lists all text files in cwd

You can also import an entire directory into the namespace. Convenient, but not really recommended.

importfile(cwd/'..'/'..', sys.modules[__name__])
importdir(cwd/'..'/'..', sys.modules[__name__])

For pathing, I recommend using "filedir", the path to the current file directory, since cwd changes frequently within large projects

from pathed import filedir

filedir/'..'/'path'/'to'/'file.txt'

Methods

filedir

Returns your current file directory as a str-like object

from pathed import filedir

print(filedir) # prints current file directory

cwd

Returns your current working directory as a str-like object

from pathed import cwd

print(cwd) # prints current working directory

new_import()

Replaces the builtins._import_ statment, this allows for normal import syntax but with relative imports

Caveats:

  • runs _init_.py if folder is imported
  • runs _init_.py if file is in the same folder as _init_.py
  • only the "from _ import _" statement is overidden
  • only *.py files are imported
  • directories are not recursively added
  • if directories are added in the _init_.py file, directories are added to namespace

Dot notation:

  • go up n-1 folders, with n representing the number of dots
from ......spicy import *
# goes up 5 folders then down one folder called spicy

from ......spicy.file import * 
# import file.py from spicy folder

from .here import *
# imports file here.py from the same directory as file
# same as:
from here import *

# name of directory or file is needed
# this is NOT possible at the moment
from .... import jalapeño
# this just imports everthing from cwd
from . import bell
# this is the right way to do it
from .pepper_file import ghost_module

importfile(path, module)

Parameters:

  • path: str or str-like object with path to directory
  • module: module that will have attributes appended to it

Returns file functions, classes, and global variables from a python file

from pathed import importfile

hard_to_import_file = importfile(cwd/'..'/'..'/'file.py')

importdir(path, module)

Parameters:

  • path: str or str-like object with path to directory
  • module: module that will have attributes appended to it

Imports *.py files from directory

Returns class with attributes named after *.py files

from pathed import importdir

hard_to_import_dir = importdir(cwd/'..'/'..')

Path(*args, custom)

Parameters:

  • *args: objects that can be turned into a string
  • custom: boolean if True, will append args to root if False, will append args to current file directory
from pathed import Path

Path('a')              # /path/to/filedir/a
Path('a', custom=True) # /a

Summary of path operations

# dir ops
Path.isdir()
Path.mkdir()
Path.rmdir()
Path.copydir()

# file ops
Path.isfile()
Path.mkfile()
Path.rmfile()
Path.copyfile()

# path ops
Path.branch()
Path.leaf()
Path.add()
Path.ls()
Path.up()
Path.find()
Path.string()
Path.exists()
Path.splitpath()

# additional ops
Path.write()
Path.read()
Path.readfast()
Path.move()

Path.isdir()

returns True if Path is a directory

from pathed import cwd

cwd.isdir() # returns True

Path.mkdir()

makes a directory called Path, throws error if Path is already a directory

from pathed import cwd

cwd.add('new_directory').mkdir()

Path.rmdir()

removes Path if Path is a directory

from pathed import cwd

new_dir = cwd/'new_directory'
new_dir.mkdir()
new_dir.rmdir()

Path.copydir(destination)

copies Path to destination if Path is a directory, returns destination as Path

from pathed import cwd

a = cwd/'a'
b = cwd/'b'
a.copydir(b/'a')
# copies directory a and places into directory b

Path.isfile(path)

returns True if Path is a file

from pathed import cwd

cwd.isfile() # returns false

Path.mkfile()

mkfile(data) writes data to Path, throws error if Path does not exist

mkfile(data, 'w') write data, makes file if Path does not exist mkfile(data, 'wb') writes bytes data to Path mkfile(data, 'a') appends data to Path

from pathed import cwd

cwd.add('new.txt').mkfile('hello world')

Path.rmfile()

removes Path if Path is a file

from pathed import cwd

textfile = cwd.add('new.txt')
textfile.mkfile('goodbye world')
textfile.rmfile()

Path.copyfile(destination)

copies Path to destination if Path is a file, returns destination as Path

from pathed import cwd

textfile = cwd.add('a.txt')
textfile.mkfile('hello world')
textfile.copyfile(textfile/'..'/'b.txt')
# copies a.txt and pastes it in the same directory as b.txt

Path.branch()

/absolute/path/to/leaf -> returns branch

from pathed import cwd

text_file = cwd.add('blank.txt').mkfile()
text_file.branch() # returns cwd

Path.leaf()

/absolute/path/to/leaf -> returns leaf

from pathed import cwd

text_file = cwd.add('blank.txt').mkfile()
text_file.branch() # returns 'blank.txt'

Path.add(*args)

does the same thing as cwd/'path'/'to'/'wonderland'

from os import cwd

# These should give the same output
cwd.add('path').add('to').add('wonderland')
cwd/'path'/'to'/'wonderland'

Path.ls()

returns [str, str, ...] of files and directories in Path

if full=True, returns [Path, Path, ...] of absolute Paths

throws error if Path doesn't exist

from pathed import cwd

cwd.ls() # lists files and directories in cwd

Path.up(num)

goes up the directory 'num' times

returns Path

from pathed import cwd

b = cwd/'a'/b'

b.up(1) # cwd/a
b.up(2) # cwd

Path.find()

find('*.py') returns [Path, Path, ...] in current Path that have the .py extension

find('**.py') returns [Path, Path, ...] in current Path and subdirectories that have the .py extension

from pathed import cwd

cwd.find(*.py) # returns list of *.py files

Path.string()

useful for printing raw Windows paths

\this\is\a\windows\path

Path.exists()

returns True if the Path exists

from pathed import cwd

cwd.exists() # should return True

Path.splitpath()

/absolute/path/to/leaf -> returns [branch, leaf]

print(branch) # /absolute/path/to print(leaf) # leaf

if full=True, returns [absolute, path, to, leaf]

from pathed import cwd

a = cwd/'a'
a.splitpath()
# returns ['path/to/cwd', 'a']
a.splitpath(full=True)
# returns ['path','to','cwd','a']

Path.write()

write(data) writes data to Path, throws error if Path does not exist

write(data, 'w') write data, makes file if Path does not exist write(data, 'wb') writes bytes data to Path write(data, 'a') appends data to Path

from pathed import cwd

cwd.add('new.txt').write('hello world')

Path.read()

read() returns text of Path if Path is a file

read('rb') returns text of byte file if Path is a file

from pathed import cwd

textfile = cwd.add('new.txt')
textfile.mkfile('hello world')
textfile.read() # hello world

Path.readfast()

good for reading the first few of lines of a VERY LARGE file

returns generator for reading large files one line at a time

file_text = Path.readfast() next(file_text) to get string of next file, throws StopIteration Error at end of file

from pathed import cwd

textfile = cwd/'new.txt'
textfile.mkfile(data='hello world')
file_gen = textfile.readfast()
next(file_gen) # hello world

Path.move()

moves Path to destination, returns destination as Path


TODO:

  • Better documentation
  • Unit tests