Pathlib is taking an os.PathLike object, and interpereting it as bool - python-3.x

The Problem
The project I'm currently working on has an inexplicable error, which either I'm too dumb to figure out, or is just that obscure and technical.
I'm trying to first locate a directory, returning the path to it, and then checking if a sub-directory exists within it.
from pathlib import Path
from os.path import isdir
from os import getenv
from subprocess import Popen
def find_dlDir():
example_dlHomeDirs = [
Path(getenv("HOME"), 'downloads'),
Path(getenv("HOME"), 'Downloads'),
]
if dlHome := getenv("DOWNLOADS_HOME") != None:
return dlHome
else:
for path in example_dlHomeDirs:
if isdir(path):
return path
return None
def dirExists(dirName: str):
if dlHome := find_dlHome() != None:
if isdir(Path(dlHome, dirName)):
return True
else:
return False
else:
print("No Downloads Folder found.\nTo resolve, create a new folder in \
your home folder with one of the following names:")
[print(name) for name in ['downloads', 'Downloads']]
exit(1)
def mkdir(path: Path, dirToMake: str):
"""
Make a directory with the name <dirToMake> at <path>
kwargs["path"]? Parent directory of <dirToMake>
kwargs["dirToMake"]? Name of the to-be-made directory
"""
Popen("mkdir", f"{str(path)}/{dirToMake}")
if __name__ == "__main__":
dir = "example"
if not dirExists(dirName=dir):
mkdir(path=getenv("DOWNLOADS_HOME"), dirToMake=dir)
The following code should-- with the filesystem below-- run: mkdir $HOME/Downloads/example.
/Users/dickssau000 ¬
----Downloads ¬
--------github.com
--------youtube.com
--------mega.nz
Instead, I get a traceback:
Traceback (most recent call last):
File "/Users/dickssau000/.local/src/github.com/Saul-Dickson/dl/test.py", line 48, in <module>
if not dirExists(dirName=dir):
File "/Users/dickssau000/.local/src/github.com/Saul-Dickson/dl/test.py", line 24, in dirExists
if isdir(Path(dlHome, dirName)):
File "/usr/local/Cellar/python#3.9/3.9.1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/pathlib.py", line 1071, in __new__
self = cls._from_parts(args, init=False)
File "/usr/local/Cellar/python#3.9/3.9.1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/pathlib.py", line 696, in _from_parts
drv, root, parts = self._parse_args(args)
File "/usr/local/Cellar/python#3.9/3.9.1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/pathlib.py", line 680, in _parse_args
a = os.fspath(a)
TypeError: expected str, bytes or os.PathLike object, not bool
I'm absolutely sure that on line 24, the variables dlHome and dirName are os.PathLike and str respectively. Neither of those variables should have the type of bool, and I'm completely stumped on how to fix it. Does anyone have a clue as to what's going on here?
python: 3.9

if dlHome := find_dlHome() != None:
...
This is equivalent to
if dlHome := (find_dlHome() != None):
...
meaning dlHome is of type bool, not the result of find_dlHome()! You want this instead:
if (dlHome := find_dlHome) is not None:
...
(you should also do None checks using is/is not instead of ==/!=)

Related

Gooey from argument to read file

So the first argument is the file to open and the second argument is the pattern (or text) to search for.
The program is made to scan a document and find items equal to "Pattern" and print the detector address in "DetectorPattern". I got this program working without Gooey but i thought about adding it for ease of use. My problem lies when the argument get passed to the "with open(filename)" line.
This is the error i get:
Traceback (most recent call last):
File "C:/Users/haral/Google Drive (synkroniseres ikke)/Programmering/Programmer/LogSearch/LogSearchGooey.py", line 42, in <module>
main()
File "C:\Users\haral\PycharmProjects\AutomateBoringStuff\venv\lib\site-packages\gooey\python_bindings\gooey_decorator.py", line 134, in <lambda>
return lambda *args, **kwargs: func(*args, **kwargs)
File "C:/Users/haral/Google Drive (synkroniseres ikke)/Programmering/Programmer/LogSearch/LogSearchGooey.py", line 27, in main
with open(filename, 'r') as reader:
TypeError: expected str, bytes or os.PathLike object, not Namespace
import os
import re
from gooey import Gooey, GooeyParser
pattern = ""
# Chosen search pattern
detectorPattern = re.compile(r'\d\d\.\d\d\d')
# Fire alarm detector pattern, etc. 03.040
filename = ""
foundDetector = []
#Gooey
def main():
parser = GooeyParser(description="Testing")
parser.add_argument(
"Filename",
help="Choose a file",
widget="FileChooser"
)
parser.add_argument(
"store",
help="Choose a pattern to search for"
)
filename = parser.parse_args()
with open(filename, 'r') as reader:
# Read and print the entire file line by line
for line in reader:
findLine = re.search(pattern, line)
if findLine is not None:
mo = detectorPattern.search(findLine.string)
mog = mo.group()
if mog not in foundDetector:
foundDetector.append(mog)
for x in foundDetector:
print(x)
if __name__ == '__main__':
main()

'CommentedMap' object has no attribute '_context_manager' during data dump with ruamel.yaml

Here's my code:
import ruamel.yaml
import pathlib
class YamlLoader:
#staticmethod
def safe_load(filename):
filepath = pathlib.Path(filename)
with open(filepath) as stream:
if ruamel.yaml.version_info < (0, 15):
data = ruamel.yaml.safe_load(stream)
else:
yml = ruamel.yaml.YAML(typ='safe', pure=True)
data = yml.load(stream)
return data
#staticmethod
def save(yaml, filename):
filepath = pathlib.Path(filename)
if ruamel.yaml.version_info < (0, 15):
ruamel.yaml.safe_dump(yaml, filepath)
else:
ruamel.yaml.YAML.dump(yaml, filepath)
my code in main.py:
data = YamlLoader.safe_load("data.yaml")
print(data)
I then get my YAML data in the variable.
However, when I then do:
YamlLoader.save(data, "output.yaml")
I get the error message:
Traceback (most recent call last): File "", line 1, in
File
"/usr/local/lib/python3.8/site-packages/ruamel/yaml/main.py", line
434, in dump
if self._context_manager: AttributeError: 'CommentedMap' object has no attribute '_context_manager'
Most likely I'm using the API in a wrong way, but I can't figure out where the issue is.
The last line of your code has a problem:
ruamel.yaml.YAML.dump(yaml, filepath)
as you are not creating an instance of YAML like you do wnen loading.
Either do:
yml = ruamel.yaml.YAML()
yml.dump(yaml, filepath)
or do :
ruamel.yaml.YAML().dump(yaml, filepath)

os.environ raises keyerror(key) from none

I am making a database gui program and I am experimenting on how to make the .db file in a different folder from the script. I am using python 3.7. Here is the function that I use:
def get_database_file():
dir_path = os.path.join(os.environ[''], 'test_database')
if not os.path.exists(dir_path):
os.makedirs(dir_path)
file_path = os.path.join(dir_path, 'test_database.db')
if not os.path.exists(file_path):
try:
conn = sqlite3.connect(file_path)
conn.execute("PRAGMA foreign_keys = 1")
except sqlite3.Error as err:
if conn:
conn.rollback() # reverse any changes before the commit
print("SQLite Error: %s" % err.args[0])
sys.exit(-1)
else:
createDatabase(file_path)
print("Finish database created")
finally:
if conn:
conn.close()
else:
return file_path
if os.stat(file_path) == 0:
print("Empty")
else:
print("Not Empty")
return file_path
When I put os.environ['APPDATA'], this function runs fine without any errors. But when I put os.environ['HOME'], it shows this error message:
Traceback (most recent call last):
File "C:/Users/jojot/PycharmProjects/CreateDatabase/gui_database.py", line 4214, in <module>
database_file = get_database_file()
File "C:/Users/jojot/PycharmProjects/CreateDatabase/gui_database.py", line 4178, in get_database_file
dir_path = os.path.join(os.environ['HOME'], 'test_database')
File "C:\Users\jojot\AppData\Local\Programs\Python\Python37-32\lib\os.py", line 678, in __getitem__
raise KeyError(key) from None
KeyError: 'HOME'
I don't understand, what did I do wrong? Please help. Thank you.
It means that you haven't set the environment variable HOME.
If you want the program not to crash if you haven't set that variable you can use:
x = os.environ.get('HOME')
print(x) # None
instead of:
x = os.environ['HOME']
# raise KeyError(key) from None
# KeyError: 'HOME'

Extract multiple files in folder - [Errno 2] No such file or directory

I'm running the following script to unzip several zipfiles from a directory:
import os
from zipfile import ZipFile
dir_name = f"C:/my_path/zip/{name}"
def main():
for item in os.listdir(dir_name):
with ZipFile(item, 'r') as zipObj:
listOfFileNames = zipObj.namelist()
for fileName in listOfFileNames:
if fileName.endswith('.csv'):
for i in fileName:
zipObj.extract(fileName, f"C:/my_path/csv/{name}")
if __name__ == '__main__':
main()
The point is that I've confirmed a hundred times that the zip files are stored in the right path but I can't run the script but I did succesfully in another computer (?)
What I am doing wrong?
EDIT:
This is the entire error message:
FileNotFoundError Traceback (most recent call last)
<ipython-input-6-7f26de4464fe> in <module>
18
19 if __name__ == '__main__':
---> 20 main()
<ipython-input-6-7f26de4464fe> in main()
10
11 for item in os.listdir(dir_name): # Iterate over the zip file
---> 12 with ZipFile(item, 'r') as zipObj: # Create a ZipFile Object and load sample.zip in it
13 listOfFileNames = zipObj.namelist() # Get a list of all archived file names from the zip
14 for fileName in listOfFileNames: # Iterate over the file names
C:\Anaconda\lib\zipfile.py in __init__(self, file, mode, compression, allowZip64)
1111 while True:
1112 try:
-> 1113 self.fp = io.open(file, filemode)
1114 except OSError:
1115 if filemode in modeDict:
FileNotFoundError: [Errno 2] No such file or directory: 'my_file.zip'
This might be the key:
Revise this line:
dir_name = f"C:/my_path/zip/{name}"
To:
dir_name = "C:/my_path/zip"
Why?
Because os.listdir is looking in a directory named "C:/my_path/zip/{name}" but it doesn't exist as the variable name has not been declared. Thus the error below, as triggered on my machine (albeit Linux, hence the non-Windows type path):
FileNotFoundError: [Errno 2] No such file or directory: '/devmt/services/{name}'
Then, apply this logic throughout the rest of your function and perhaps use os.path.join to stitch your path and filenames together.

Using 'dpkg' in Python causes OSError: [Errno 9] Bad file descriptor

I built a script to find the most recent version of Visual Studio Code, download it and install it on an Ubuntu machine with dpkg. I haven't found a decent Python library for doing this and am using subprocess.call() to invoke a Shell command. This certainly may not be the best way to do it, but this is also a learning project.
It successfully downloads the file and places it in my ~/Downloads dir. When I try to invoke the subprocess.call(), it spits back 'OSError: [Errno 9] Bad file descriptor'
I know my command string is correct. I can invoke it just fine from the CLI. But it doesn't work when called through subprocess.
Any advice on doing this more efficiently is welcome.
"""
Python 3 script
Downloads the latest .deb package for installing VSCode, and installs it
"""
import os # used to direct where to save downloaded file
import subprocess # used to derive filepath of CLI arg
import requests # py3 only
import platform # used to detect the OS
from urllib.request import urlopen, ContentTooShortError, urlretrieve # py3 version of 'import urllib2'
HOME = os.path.expanduser('~')
filePath = HOME + "/Downloads"
fileName = 'vs_code_most_recent_amd64.deb'
outputName = os.path.join(filePath, fileName)
alreadyDownloaded = False
# used in subprocess calls to suppress stdout or stderr
pipeToDevNull = open(os.devnull, 'w')
def IsDownloadable(url):
"""
Check of the link passed in is a downloadable file. Used to shortcut the
processing so that it doesn't attempt to download a URL that isn't
downloadable. Returns True or False.
"""
h = requests.head(url, allow_redirects=True)
header = h.headers
contentType = header.get('content-type')
if 'text' in contentType.lower():
return False
if 'html' in contentType.lower():
return False
return True
def DownloadVSCodePkg(url):
"""
Downloads the file at the specified URL, save it as the above-defined filename
"""
u = urlopen(url)
f = open(outputName, 'wb')
meta = u.info()
fileSize = int(meta.get_all("Content-Length")[0])
fileSizeDL = 0
#blockSize = 8192
blockSize = 16384
while True:
buffer = u.read(blockSize)
if not buffer:
break
fileSizeDL += len(buffer)
f.write(buffer)
status = r"%10d Bytes [%3.2f%%]" % (fileSizeDL, fileSizeDL * 100. / fileSize)
status = status + chr(8)*(len(status)+1)
print("Downloading: {0}".format(status), end="\r", flush=True)
print("Downloading: {0}".format(status))
print("Downloaded: {0}".format(fileName))
f.close()
del f
def CheckDownloadSuccess():
"""
returns bool value if the file we want is in the dir specified
"""
try:
subprocess.check_call("ls " + outputName, stdout=pipeToDevNull, stderr=pipeToDevNull, shell=True)
return True
except subprocess.CalledProcessError:
return False
def UnpackAndInstall():
"""
Invokes dpkg from the linux shell and installs VSCode.
"""
#Detect OS
linuxDistro = platform.linux_distribution()
OSType = linuxDistro[0]
if OSType == 'Ubuntu':
from apt.debfile import DebPackage
pkg = DebPackage(outputName)
command = 'sudo dpkg -i ' + outputName
#The thing that attempts to unpack:
try:
subprocess.check_call(command, stdout=subprocess.STDOUT, stderr=subprocess.STDOUT, shell=True)
except subprocess.CalledProcessError:
print("Install Failed.")
def main():
url = 'https://go.microsoft.com/fwlink/?LinkID=760868'
alreadyDownloaded = CheckDownloadSuccess()
if alreadyDownloaded is False:
if IsDownloadable(url):
DownloadVSCodePkg(url)
# check if the download succeeded, if file doesn't already exist.
if CheckDownloadSuccess():
print("Download Successful!\nFile location => " + outputName)
else:
print("Download Failed...")
else:
print('Link broken: need to update the package resource link.')
else:
print("File already exists.")
UnpackAndInstall()
if __name__ == "__main__":
main()
Here is the traceback and the error from the CLI:
$ python3 setupVSCode.py
Traceback (most recent call last):
File "setupVSCode.py", line 192, in <module>
main()
File "setupVSCode.py", line 189, in main
UnpackAndInstall()
File "setupVSCode.py", line 95, in UnpackAndInstall
subprocess.call(command, stdout=subprocess.STDOUT, stderr=subprocess.STDOUT, shell=True)
File "/usr/lib/python3.6/subprocess.py", line 267, in call
with Popen(*popenargs, **kwargs) as p:
File "/usr/lib/python3.6/subprocess.py", line 709, in __init__
restore_signals, start_new_session)
File "/usr/lib/python3.6/subprocess.py", line 1344, in _execute_child
raise child_exception_type(errno_num, err_msg, err_filename)
OSError: [Errno 9] Bad file descriptor
os.path.expanduser('~') is going to return something like: 'C:\\Users\\user.name' which you're appending '/Downloads', resulting in a bad path like: 'C:\\Users\\user.name/Downloads\\'
Instead of:
filePath = HOME + "/Downloads"
Do:
filePath = HOME + "\Downloads"
Or preferably:
filePath = os.path.join(HOME, 'Downloads')
After speaking with #Steve, I tried removing the output redirectors on the subprocess.call().
The advice on removing all slashes in path construction and instead using "os.path.join()" has been implemented and will be followed as a best practice from here on.
Since the command as constructed worked fine from the CLI, it was a matter of thinking about what the subprocess.call() did that was different. It redirected output. With that removed things work fine
It now looks like so:
HOME = os.path.expanduser('~')
filePath = os.path.join(HOME, "Downloads")
fileName = 'vs_code_most_recent_amd64.deb'
outputName = os.path.join(filePath, fileName)
alreadyDownloaded = False
...
command = 'sudo dpkg -i ' + outputName
try:
subprocess.check_call(command, shell=True)
except subprocess.CalledProcessError:
print("Install Failed.")

Resources