"""Simplified input and output."""
import contextlib
import os
import pathlib
import re
import shutil
import sys
from io import StringIO
from pathlib import Path
import requests
from .grep import *
from .head import *
from .sed import *
from .tail import *
[docs]def read(to_be_read: str):
"""
Open either a file or URI and return the content.
Reads the file ``to_be_read``.
Parameters
----------
fname : str
file name.
Returns
-------
str
content of the file.
Examples
--------
>>> read("nonexistent.txt")
''
>>> write("test.out","hi")
>>> read("test.out")
'hi'
>>> read("https://raw.githubusercontent.com/APN-Pucky/smpl_io/master/LICENSE").split("\\n")[0].strip()
'GNU GENERAL PUBLIC LICENSE'
"""
if to_be_read.startswith("http"):
return requests.get(to_be_read).text
else:
if not os.path.exists(to_be_read):
return ""
with open(to_be_read, "r") as f:
return f.read()
[docs]@contextlib.contextmanager
def pushd(new_dir, tmp=False, cd=True):
"""
Move to a new directory and return to the previous one after context.
Parameters
----------
new_dir : str
new directory.
tmp: bool, optional
create the directory if it does not exist and delete after context, by default False
cd: bool, optional
change to (new) directory, by default True
Examples
--------
>>> import os
>>> p = os.getcwd()
>>> with pushd("tmptest",tmp=True):
... pp = os.getcwd()
... pp.startswith(p) and pp.endswith("tmptest")
True
>>> import os
>>> p = os.getcwd()
>>> with pushd("tmptest",tmp=True,):
... pp = os.getcwd()
... pp.startswith(p) and pp.endswith("tmptest")
True
"""
previous_dir = os.getcwd()
if tmp:
abspath = os.path.abspath(new_dir)
os.makedirs(abspath, exist_ok=False)
if cd:
os.chdir(new_dir)
try:
yield
finally:
if cd:
os.chdir(previous_dir)
if tmp:
shutil.rmtree(abspath, ignore_errors=True)
[docs]def glob_re(pattern, path):
"""
Returns all strings that match the regex pattern.
Parameters
----------
pattern : str
regex pattern.
path : str
path to search in.
Returns
-------
list
list of filenames that match the regex pattern.
Examples
--------
>>> import os
>>> with pushd("tmptest",tmp=True,cd=False):
... write("tmptest/test.txt","hi\\nho1\\n2\\n3\\n4\\n")
... glob_re(".*[xt][xt][xt]", "tmptest")
['test.txt']
"""
return list(filter(re.compile(pattern).match, os.listdir(path)))
[docs]def write(destination, content, mode="w+", create_dir=True):
"""
Write to file by string or writable :obj:`destiantion`.
Parameters
----------
destination : str, writeable
destination to write to.
content : str
text to be written.
mode : str
mode to open the file.
Default is 'w+' (write and read).
create_dir : bool
create directory if it does not exist.
Examples
--------
>>> write(sys.stdout,"hi")
hi
>>> write("test.out","hi")
>>> read("test.out")
'hi'
"""
# TODO add http and other string based write methodes
if isinstance(destination, str):
if create_dir:
try:
os.makedirs(os.path.dirname(destination), exist_ok=True)
except FileNotFoundError:
# this happens when the path is a local path, ie. a single file
pass
with open(destination, mode) as f:
f.write(content)
else:
destination.write(content)
[docs]def append(destination, content, mode="a+"):
"""
Appends to file by string or writable :obj:`destiantion`.
Parameters
----------
destination : str, writeable
destination to write to.
content : str
text to be written.
mode : str
mode to open the file.
Default is 'a+' (append and read).
Examples
--------
>>> append(sys.stdout,"hi")
hi
"""
write(destination, content, mode)
[docs]def gf(i=3):
"""
Scientific number format.
Parameters
----------
i : int
Number of digits.
Returns
-------
str
Scientific number format string.
Examples
--------
>>> gf(2)
'{0:.2g}'
>>> gf(2).format(789234578934)
'7.9e+11'
>>> gf(5).format(789234578934)
'7.8923e+11'
"""
return "{0:." + str(i) + "g}"
[docs]def find_file(fname, up=0):
"""
Searches for ``fname`` in all down folders or up folder to given order respectively.
Parameters
----------
fname : str
file name.
up : int
number of up folders to search.
Returns
-------
str
path to the file.
Examples
--------
>>> import os
>>> find_file("io.py",0)
'smpl_io/io.py'
>>> os.chdir("smpl_io")
>>> find_file("io.py",0)
'io.py'
>>> find_file("Makefile",1)
'../Makefile'
"""
p = Path(os.path.abspath(fname)).parent
n = Path(os.path.abspath(fname)).name
if (p / n).is_file():
return str(os.path.relpath(p / n, os.path.abspath(".")))
for _ in range(up):
p = p.parent
for f in p.rglob(n):
if f.is_file():
return str(os.path.relpath(f, os.path.abspath(".")))
return None
[docs]def pwd() -> str:
"""
Returns the path to the path of current file (in linux format).
Returns
-------
str
path to the path of current file.
Examples
--------
>>> pwd().endswith("smpl_io")
True
"""
# pwd_ = "/".join(debug.get_line_number_file(split=False, _back=1)[1].split("/")[:-1])
path = Path(__file__).parent.absolute()
return str(path)
[docs]def import_path(path="../.."):
"""
Adds ``path`` to the ``sys.path``.
Parameters
----------
path : str
path to add.
Examples
--------
>>> import_path('../../smpl')
"""
sys.path.insert(0, os.path.abspath(path))
[docs]def mkdirs(fn):
"""
Creates the neccessary directories above ``fn``.
Parameters
----------
fn : str
file name.
Examples
--------
>>> mkdirs("test.out")
"""
pathlib.Path(fn).parent.mkdir(parents=True, exist_ok=True)
[docs]def pr(a, nnl=False):
"""
Prints the passed ``a``.
Parameters
----------
nnl : bool
no-new-line
Returns
-------
a : any
unchanged ``a``.
Examples
--------
>>> 5 + pr(4)
4
9
>>> 5 + pr(4, nnl=True)
49
"""
if nnl:
print(a, end="")
else:
print(a)
return a
[docs]def files(ending, folder="."):
"""
Get all the files in ``folder`` ending with ``ending``.
Parameters
----------
folder : str
folder name.
ending : str
ending of the files.
Returns
-------
list
list of files.
Examples
--------
>>> files(".ini")
[(0, 'pytest', './pytest.ini')]
"""
r = []
i = 0
for file in os.scandir(folder):
if file.path.endswith(ending):
r.append((i, os.path.splitext(os.path.basename(file.path))[0], file.path))
i = i + 1
return r
[docs]def pn(a, nnl=False):
"""
Find variable with the same value as ``a`` in globals.
Then print its name and value.
Parameters
----------
a : any
variable to find.
nnl : bool
no-new-line
Returns
-------
a : any
unchanged ``a``.
"""
gl = globals()
for key in gl:
if gl[key] == a:
print(key)
if nnl:
print(f"{a.__name__}={a}", end="")
else:
print(f"{a.__name__}={a}")
return a
[docs]def remove(file):
"""
Removes ``file``.
Parameters
----------
file : str
file name.
Examples
--------
>>> remove("test.out")
"""
if os.path.exists(file):
os.remove(file)
alias_open = open
[docs]def open(to_be_read: str, mode="r"):
"""
Return opened buffer of either file or URI.
Parameters
----------
to_be_read : str
file name or URI.
mode : str
mode to open the file.
Returns
-------
buffer
opened buffer.
"""
if mode != "r":
return alias_open(to_be_read, mode)
if to_be_read.startswith("http"):
return StringIO(requests.get(to_be_read).text)
else:
if not os.path.exists(to_be_read):
return StringIO("")
return alias_open(to_be_read, "r")