how to get the caller's filename, method name in python - python

for example, a.boo method calls b.foo method. In b.foo method, how can I get a's file name (I don't want to pass __file__ to b.foo method)...

You can use the inspect module to achieve this:
frame = inspect.stack()[1]
module = inspect.getmodule(frame[0])
filename = module.__file__

Python 3.5+
One-liner
To get the full filename (with path and file extension), use in the callee:
import inspect
filename = inspect.stack()[1].filename
Full filename vs filename only
To retrieve the caller's filename use inspect.stack(). Additionally, the following code also trims the path at the beginning and the file extension at the end of the full filename:
# Callee.py
import inspect
import os.path
def get_caller_info():
# first get the full filename (including path and file extension)
caller_frame = inspect.stack()[1]
caller_filename_full = caller_frame.filename
# now get rid of the directory (via basename)
# then split filename and extension (via splitext)
caller_filename_only = os.path.splitext(os.path.basename(caller_filename_full))[0]
# return both filename versions as tuple
return caller_filename_full, caller_filename_only
It can then be used like so:
# Caller.py
import callee
filename_full, filename_only = callee.get_caller_info()
print(f"> Filename full: {filename_full}")
print(f"> Filename only: {filename_only}")
# Output
# > Filename full: /workspaces/python/caller_filename/caller.py
# > Filename only: caller
Official docs
os.path.basename(): to remove the path from the filename (still includes the extension)
os.path.splitext(): to split the the filename and the file extension

Inspired by ThiefMaster's answer but works also if inspect.getmodule() returns None:
frame = inspect.stack()[1]
filename = frame[0].f_code.co_filename

This can be done with the inspect module, specifically inspect.stack:
import inspect
import os.path
def get_caller_filepath():
# get the caller's stack frame and extract its file path
frame_info = inspect.stack()[1]
filepath = frame_info[1] # in python 3.5+, you can use frame_info.filename
del frame_info # drop the reference to the stack frame to avoid reference cycles
# make the path absolute (optional)
filepath = os.path.abspath(filepath)
return filepath
Demonstration:
import b
print(b.get_caller_filepath())
# output: D:\Users\Aran-Fey\a.py

you can use the traceback module:
import traceback
and you can print the back trace like this:
print traceback.format_stack()
I haven't used this in years, but this should be enough to get you started.

Reading all these solutions, it seems like this works as well?
import inspect
print inspect.stack()[1][1]
The second item in the frame already is the file name of the caller, or is this not robust?

Related

How to replace a part of a File Path in Python

import os.path
original = input(str("Filepath:"))
filename = os.path.basename(original)
print(filename)
target = r'C:\Users\Admin\Desktop\transfer\filename'
path = filename.replace('filename', filename)
print(path)
I have a problem with getting new target path... I need to copy original file and paste it to new directory, that is always the same and the name must stay the same as it was in previous directory, I was trying to do it by code on top but it doesn't work, only thing I need to know is how to replace name of the Path file at the end. (Example: r'C:\Users\Admin\Desktop\Directory2\***' and replace *** with filename of first file)
Considering your code, if you want to change C:\Users\Admin\Desktop\transfer\filename into C:\Users\Admin\Desktop\transfer\{new filename} you need to call replace() function on «target» variable, not on the «filename» variable.
So your code would look something like:
import os.path
original = input(str("Filepath:"))
filename = os.path.basename(original)
target = r'C:\Users\Admin\Desktop\transfer\filename'
path = target.replace('filename', filename)
On entering D:\Documents\program.py, the output is C:\Users\Admin\Desktop\transfer\program.py

Change filename prefix in Path PosixPath object

I need to change a prefix for a current file.
An example would look as follows:
from pathlib import Path
file = Path('/Users/my_name/PYTHON/Playing_Around/testing_lm.py')
# Current file with destination
print(file)
# Prefix to be used
file_prexif = 'A'
# Hardcoding wanted results.
Path('/Users/my_name/PYTHON/Playing_Around/A_testing_lm.py')
As can be seen hardcoding it is easy. However is there a way to automate this step?
There is a pseudo - idea of what I want to do:
str(file).split('/')[-1] = str(file_prexif) + str('_') + str(file).split('/')[-1]
I only want to change last element of PosixPath file. However it is not possible to change only last element of string
file.stem accesses the base name of the file without extension.
file.with_stem() (added in Python 3.9) returns an updated Path with a new stem:
from pathlib import Path
file = Path('/Users/my_name/PYTHON/Playing_Around/testing_lm.py')
print(file.with_stem(f'A_{file.stem}'))
\Users\my_name\PYTHON\Playing_Around\A_testing_lm.py
Use file.parent to get the parent of the path and file.name to get the final path component, excluding the drive and root.
from pathlib import Path
file = Path('/Users/my_name/PYTHON/Playing_Around/testing_lm.py')
file_prexif_lst = ['A','B','C']
for prefix in file_prexif_lst:
p = file.parent.joinpath(f'{prefix}_{file.name}')
print(p)
/Users/my_name/PYTHON/Playing_Around/A_testing_lm.py
/Users/my_name/PYTHON/Playing_Around/B_testing_lm.py
/Users/my_name/PYTHON/Playing_Around/C_testing_lm.py

Making a method select the location of a file using recursion

I'm trying to create a recursive method; the goal of this method is to find the deepest directory that could contain this file and determine if the file belongs here. If the file does I want it to return the full file location. the first method was the original, but it didn't work with a return statement.
import zipfile, lxml.etree, os, xml.etree.ElementTree as ET, sys, shutil, re
from docx import Document
path = #Directory where you want files sorted to
Orignial method
def findSubDir(file,dire):
tempPath = os.path.join(path,dire.replace(" ",""))
for dirs in os.listdir(tempPath):
if os.path.isdir(os.path.join(tempPath,dirs)):
for names in re.findall('[A-Z][^A-Z]+', dirs):
if names in file:
print(os.path.join(tempPath,dirs))
findSubDir(file,os.path.join(dire,dirs))
Modified method
def reFindSubDir(file,dire):
tempPath = os.path.join(path,dire.replace(" ",""))
dirs = [dirs for dirs in os.listdir(tempPath)
if os.path.isdir(os.path.join(tempPath,dirs))]
if(any(os.path.isdir(os.path.join(tempPath,dirs)) for dirs in os.listdir(tempPath))):
print("passed")
folder = "".join([names for dires in dirs for names in re.findall('[A-Z][^A-Z]+', dires)if names in file])
print("folder")
if folder != "":
reFindSubDir(file,os.path.join(dire,folder))
else:
print("Steve2")
return "Steve"
else:
print(tempPath)
return(tempPath)
There are multiple issues I immediately see in your code. First, in your original attempt it appears you are missing a base case. Second, you're not returning anything in your recursive case.
Try using this first without your regex, then you can incorporate it into your code.
def findSubDir(file, dire=""):
tempPath = os.path.join(path, dire)
for dirs in os.listdir(tempPath):
if os.path.isdir(os.path.join(tempPath, dirs)):
return findSubDir(file,os.path.join(tempPath,dirs))
if dirs == file:
return os.path.join(tempPath, file)

Rename files in multiple directories

I have files named the same in multiple directories. I wanted to change their names, so they would correspond to the unique id of the directory that they are in.
'*' represents unique identifier, like '067' for example
The filename is always 'NoAdapter_len25.truncated_sorted.fastq'
I wanted the filename in each directory to be '*NoAdapter_len25.truncated_sorted.fastq', where * stands for the unique identifier
Here is the the error I'm getting:
Traceback (most recent call last):
File "change_names.py", line 19, in <module>
rename(name, new_name)
TypeError: Can't convert '_io.TextIOWrapper' object to str implicitly
Here's the code that produces it:
from glob import glob
import re
from os import rename
#path = "/home/users/screening/results_Sample_*_hg38_hg19/N*"
files = glob(path)
for f in files:
with open(f) as name:
sample_id = f.partition('results_')[-1].rpartition('hg38_hg19')[0]
#print(sample_id)
back = f[-38:]
new_name = sample_id + back
rename(name, new_name)
You have a few problems:
You're opening a file for no apparent reason (it confirms the file exists and is readable at open time, but even with an open handle, the name could be moved or deleted between that and the rename, so you aren't preventing any race conditions)
You're passing the opened file object to os.rename, but os.rename takes str, not file-like objects
You're doing a lot of "magic" manipulations of the path, instead of using appropriate os.path functions
Try this to simplify the code. I included some inline comments when I'm doing what your example does, but it doesn't make a lot of sense (or it's poor form):
for path in files: # path, not f; f is usually placeholder for file-like object
filedir, filename = os.path.split(path)
parentdir = os.path.dirname(filedir)
# Strip parentdir name to get 'Sample_*_' per provided code; is this what you wanted?
# Question text seems like you only wanted the '*' part.
sample_id = parentdir.replace('results_', '').replace('hg38_hg19', '')
# Large magic numbers are code smell; if the name is a fixed name,
# just use it directly as a string literal
# If the name should be "whatever the file is named", use filename unsliced
# If you absolutely need a fixed length (to allow reruns or something)
# you might do keepnamelen = len('NoAdapter_len25.truncated_sorted.fastq')
# outside the loop, and do f[-keepnamelen:] inside the loop so it's not
# just a largish magic number
back = filename[-38:]
new_name = sample_id + back
new_path = os.path.join(filedir, new_name)
rename(path, new_path)
You feed rename a file (name) and a filename, it needs two filenames. To get from a file to its filename, you can do this
old_filename = os.path.abspath(name.name)

Getting the current path from where a module is being called

I have a python module I built myself and it's located in /usr/local/lib/python3.4/site-packages/my_module1. In the module I have this:
class Class1(object);
def __init__(self, file_name):
self.file_name = file_name # how can I get the full path of file_name?
How do I get the full of file_name? That is, if it's just a file name without a path, then append the current folder from where the module is being called. Otherwise, treat the file name as a full path.
# /users/me/my_test.py
from module1 import Class1
c = Class1('my_file1.txt') # it should become /users/me/my_file1.txt
c1 = Class1('/some_other_path/my_file1.txt') # it should become /some_other_path/my_file1.txt/users/me/my_file1.txt
Update: Sorry about that. I mis-read your question. All you need to do is pass filename so os.path.abspath().
Example:
import os
filename = os.path.abspath(filename)
To cater for your 2nd case (which I find quite weird):
import os
if os.path.isabs(filenaem):
filename os.path.join(
os.path.join(
filename,
os.getcwd()
),
os.path.basename(filename)
)

Categories