Platform independent path concatenation using "/" , "\"? - python

In python I have variables base_dir and filename. I would like to concatenate them to obtain fullpath. But under windows I should use \ and for POSIX / .
fullpath = "%s/%s" % ( base_dir, filename ) # for Linux
How can I make this platform independent?

You want to use os.path.join() for this.
The strength of using this rather than string concatenation etc is that it is aware of the various OS specific issues, such as path separators. Examples:
import os
Under Windows 7:
base_dir = r'c:\bla\bing'
filename = r'data.txt'
os.path.join(base_dir, filename)
'c:\\bla\\bing\\data.txt'
Under Linux:
base_dir = '/bla/bing'
filename = 'data.txt'
os.path.join(base_dir, filename)
'/bla/bing/data.txt'
The os module contains many useful methods for directory, path manipulation and finding out OS specific information, such as the separator used in paths via os.sep

Use os.path.join():
import os
fullpath = os.path.join(base_dir, filename)
The os.path module contains all of the methods you should need for platform independent path manipulation, but in case you need to know what the path separator is on the current platform you can use os.sep.

Digging up an old question here, but on Python 3.4+ you can use pathlib operators:
from pathlib import Path
# evaluates to ./src/cool-code/coolest-code.py on Mac
concatenated_path = Path("./src") / "cool-code\\coolest-code.py"
It's potentially more readable than os.path.join() if you are fortunate enough to be running a recent version of Python. But, you also tradeoff compatibility with older versions of Python if you have to run your code in, say, a rigid or legacy environment.

import os
path = os.path.join("foo", "bar")
path = os.path.join("foo", "bar", "alice", "bob") # More than 2 params allowed.

I've made a helper class for this:
import os
class u(str):
"""
Class to deal with urls concat.
"""
def __init__(self, url):
self.url = str(url)
def __add__(self, other):
if isinstance(other, u):
return u(os.path.join(self.url, other.url))
else:
return u(os.path.join(self.url, other))
def __unicode__(self):
return self.url
def __repr__(self):
return self.url
The usage is:
a = u("http://some/path")
b = a + "and/some/another/path" # http://some/path/and/some/another/path

Thanks for this. For anyone else who sees this using fbs or pyinstaller and frozen apps.
I can use the below which works perfect now.
target_db = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), "sqlite_example.db")
I was doing this fugliness before which was obviously not ideal.
if platform == 'Windows':
target_db = (os.path.abspath(os.path.dirname(sys.argv[0])) + "\\" + "sqlite_example.db")
if platform == 'Linux' or 'MAC':
target_db = (os.path.abspath(os.path.dirname(sys.argv[0])) + "/" + "sqlite_example.db")
target_db_path = target_db
print(target_db_path)

Related

How to include a variable in 'r' raw string

I've created a new variable for user path, but not sure how to add in to the following.
import os, pwd
path=os.getcwd()
#crif0 = r '/abc/crif/gpio_mem_0_crif.xml' - original
crif0 = r (path+ '/crif/gpio_mem_0_crif.xml') - I tried with this but doesn't work
As mentioned in the comments, r is a literal prefix which you cannot apply to anything but string literals, so path + r'/crif/...' is enough. However, in this particular case when you need to compose a file path, I'd use the standard library, which makes the code more portable:
import os
path = os.getcwd()
crif0 = os.path.join(path, 'crif', 'gpio_mem_0_crif.xml')
or, in a more modern way using path objects rather than strings:
from pathlib import Path
crif0 = Path.cwd() / 'crif' / 'gpio_mem0_crif.xml'

How to join paths nice way?

What I have:
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
filepath = 'files/one.txt'
request_path = os.path.join(BASE_DIR, filepath)
print(request_path, filepath, BASE_DIR)
And it prints
/files/one.txt /files/one.txt /home/pavel/Dev/AiPOSiZI/Lab_1
what means paths weren't joined.
But
os.path.join('/home/pavel/Dev/AiPOSiZI/Lab_1/', 'files/one.txt')
(I've added / to the end of /home/... and removed / from the beginning of /files/...)
works well.
I could just manually add and remove / from paths but I wonder if there is any more elegant way to join them
On python 3.8.0 it seem to work as expected.
import os
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
filepath = 'files/one.txt'
request_path = os.path.join(BASE_DIR, filepath)
print(request_path, filepath, BASE_DIR)
$ python -V
Python 3.8.0
$ python /tmp/a.py
/tmp/files/one.txt files/one.txt /tmp
You could use the f-string in python (see documentation).
In your case:
request_path = f"{BASE_DIR}/{filepath}"
Be aware that you need an extra slash "/" between your file location variables (BASE_DIR and filepath) to complete the full file location properly.
Hope this helps!

Creating Modules and using them

I am playing around with creating modules.I have two python scripts.The first (the module) has:
def abspath(relpath):
import os
absdir = os.path.realpath('__file__')
absdir = absdir.split('_')[0].replace('\\', '/')
filename = str(absdir + relpath )
print (filename)
return filename;
The second file (main) has:
import file_tools as ft
filename = ft.abspath('some/path/')
When I run Main, filename appears empty (Type:None). I have run the filename = abspath(etc) within the 'module', and it works. Clearly, I am missing something here!!
and doing this, so any help would be useful.
Thank's all.
MT
The problem lies in how you're finding the working directory; the preferred method being os.getcwd() (or os.getcwdb for Unix compatibility). Using that, we can see that your function boils down to:
def abspath(relpath):
return os.path.join(os.getcwd(), relpath)

Build the full path filename in Python

I need to pass a file path name to a module. How do I build the file path from a directory name, base filename, and a file format string?
The directory may or may not exist at the time of call.
For example:
dir_name='/home/me/dev/my_reports'
base_filename='daily_report'
format = 'pdf'
I need to create a string '/home/me/dev/my_reports/daily_report.pdf'
Concatenating the pieces manually doesn't seem to be a good way. I tried os.path.join:
join(dir_name,base_filename,format)
but it gives
/home/me/dev/my_reports/daily_report/pdf
This works fine:
os.path.join(dir_name, base_filename + '.' + filename_suffix)
Keep in mind that os.path.join() exists only because different operating systems use different path separator characters. It smooths over that difference so cross-platform code doesn't have to be cluttered with special cases for each OS. There is no need to do this for file name "extensions" (see footnote) because they are always preceded by a dot character, on every OS.
If using a function anyway makes you feel better (and you like needlessly complicating your code), you can do this:
os.path.join(dir_name, '.'.join((base_filename, filename_suffix)))
If you prefer to keep your code clean, simply include the dot in the suffix:
suffix = '.pdf'
os.path.join(dir_name, base_filename + suffix)
That approach also happens to be compatible with the suffix conventions in pathlib, which was introduced in python 3.4 a few years after this question was asked. New code that doesn't require backward compatibility can do this:
suffix = '.pdf'
pathlib.PurePath(dir_name, base_filename + suffix)
You might be tempted to use the shorter Path() instead of PurePath() if you're only handling paths for the local OS. I would question that choice, given the cross-platform issues behind the original question.
Warning: Do not use pathlib's with_suffix() for this purpose. That method will corrupt base_filename if it ever contains a dot.
Footnote: Outside of Microsoft operating systems, there is no such thing as a file name "extension". Its presence on Windows comes from MS-DOS and FAT, which borrowed it from CP/M, which has been dead for decades. That dot-plus-three-letters that many of us are accustomed to seeing is just part of the file name on every other modern OS, where it has no built-in meaning.
If you are fortunate enough to be running Python 3.4+, you can use pathlib:
>>> from pathlib import Path
>>> dirname = '/home/reports'
>>> filename = 'daily'
>>> suffix = '.pdf'
>>> Path(dirname, filename).with_suffix(suffix)
PosixPath('/home/reports/daily.pdf')
Um, why not just:
>>> import os
>>> os.path.join(dir_name, base_filename + "." + format)
'/home/me/dev/my_reports/daily_report.pdf'
Is not it better to add the format in the base filename?
dir_name='/home/me/dev/my_reports/'
base_filename='daily_report.pdf'
os.path.join(dir_name, base_filename)
Just use os.path.join to join your path with the filename and extension. Use sys.argv to access arguments passed to the script when executing it:
#!/usr/bin/env python3
# coding: utf-8
# import netCDF4 as nc
import numpy as np
import numpy.ma as ma
import csv as csv
import os.path
import sys
basedir = '/data/reu_data/soil_moisture/'
suffix = 'nc'
def read_fid(filename):
fid = nc.MFDataset(filename,'r')
fid.close()
return fid
def read_var(file, varname):
fid = nc.Dataset(file, 'r')
out = fid.variables[varname][:]
fid.close()
return out
if __name__ == '__main__':
if len(sys.argv) < 2:
print('Please specify a year')
else:
filename = os.path.join(basedir, '.'.join((sys.argv[1], suffix)))
time = read_var(ncf, 'time')
lat = read_var(ncf, 'lat')
lon = read_var(ncf, 'lon')
soil = read_var(ncf, 'soilw')
Simply run the script like:
# on windows-based systems
python script.py year
# on unix-based systems
./script.py year
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
TEMPLATE_PATH = Path.joinpath(BASE_DIR,"templates")
print(TEMPLATE_PATH)
Adding code below for better understanding:
import os
def createfile(name, location, extension):
print(name, extension, location)
#starting creating a file with some dummy contents
path = os.path.join(location, name + '.' + extension)
f = open(path, "a")
f.write("Your contents!! or whatever you want to put inside this file.")
f.close()
print("File creation is successful!!")
def readfile(name, location, extension):
#open and read the file after the appending:
path = os.path.join(location, name + '.' + extension)
f = open(path, "r")
print(f.read())
#pass the parameters here
createfile('test','./','txt')
readfile('test','./','txt')

How to determine whether specified file is placed inside of the specified folder?

Let's say I have two paths: the first one (may be file or folder path): file_path, and the second one (may only be a folder path): folder_path. And I want to determine whether an object collocated with file_path is inside of the object collocated with folder_path.
I have an idea of doing this:
import os
...
def is_inside(file_path, folder_path):
full_file_path = os.path.realpath(file_path)
full_folder_path = os.path.realpath(folder_path)
return full_folder_path.startswith(full_file_path)
but I'm afraid there are some pitfalls in this approach. Also I think there must be a prettier way to do this.
The solution must work on Linux but it would be great if you propose me some cross-platform trick.
Use os.path.commonprefix. Here's an example based on your idea.
import os.path as _osp
def is_inside(file_path, folder_path):
full_file_path = _osp.realpath(file_path)
full_folder_path = _osp.realpath(folder_path)
return _osp.commonprefix([full_file_path, full_folder_path]) == \
full_folder_path
Parse the file name from the file path and do
os.path.exists(full_folder_path + '/' + file_name)

Categories

Resources