How to have persistent storage for a PYPI package - python

I have a pypi package called collectiondbf which connects to an API with a user entered API key. It is used in a directory to download files like so:
python -m collectiondbf [myargumentshere..]
I know this should be basic knowledge, but I'm really stuck on the question:
How can I save the keys users give me in a meaningful way so that they do not have to enter them every time?
I would like to use the following solution using a config.json file, but how would I know the location of this file if my package will be moving directories?
Here is how I would like to user it but obviously it won't work since the working directory will change
import json
if user_inputed_keys:
with open('config.json', 'w') as f:
json.dump({'api_key': api_key}, f)

Most common operating systems have the concept of an application directory that belongs to every user who has an account on the system. This directory allows said user to create and read, for example, config files and settings.
So, all you need to do is make a list of all distros that you want to support, find out where they like to put user application files, and have a big old if..elif..else chain to open the appropriate directory.
Or use appdirs, which does exactly that already:
from pathlib import Path
import json
import appdirs
CONFIG_DIR = Path(appdirs.user_config_dir(appname='collectiondbf')) # magic
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
config = CONFIG_DIR / 'config.json'
if not config.exists():
with config.open('w') as f:
json.dumps(get_key_from_user(), f)
with config.open('r') as f:
keys = json.load(f) # now 'keys' can safely be imported from this module

Related

FILE (.json) NOT FOUND

This is my directory where i have activated a virtual environment:
I'm working on a flask project to create a rest API, and I have a JSON credential file (google vision file), but when I run the code it says file not found even if it's in the same directory. I've activated a virtualenv for this particular project. mainone.py is the code I'm trying to run.
This is the error I am getting:
"File {} was not found.".format(filename)
google.auth.exceptions.DefaultCredentialsError: File date_scanner.json was not found.
And this is the code block where I am using accessing the particular file:
from flask import Flask,request,jsonify
import os,io,re,glob,base64
from google.cloud import vision
from google.cloud.vision import types
from PIL import Image
os.environ['GOOGLE_APPLICATION_CREDENTIALS']=r'date_scanner.json'
client=vision.ImageAnnotatorClient()
This is likely because the "working folder" of the Python process is not the same as where the file is located. There is a small (albeit convoluted) way to generate filenames which will always work:
Use __file__ to get the filename of the Python file where this is called.
Strip the filename from that path using os.path.dirname
Append the filename that you want to open using os.path.join
from os.path import dirname, join
def get_filename(filename):
here = dirname(__file__)
output = join(here, filename)
return output
Some food for thought
However, in this case there is something you should be careful about: The file contains security credentials and should not live in your source code. So while the above example will solve the immediate problem, the security of this should be addressed.
The best way for this is to externalise the filename of the credentials file into a config file. Say, for example you would use a .ini file as config file, you could have something like this in your code somewhere:
config = configparser.ConfigParser()
config.read('config.ini')
vision_creds_file = config.get('google_vision', 'filename')
and then place the following into config.ini:
[google_vision]
filename = /path/to/filename.json
This still requires you to place the config.ini file somewhere which should be documented in your application, as you still cannot add this to the source code (maybe a sample file with defaults).

What is the standard way to use Python module os to specify the paths for third parties?

(I'm not used to writing python programs for other users to use, so hopefully this question is appropriate.)
My users will download a file generic_file.csv and let's assume that this file will be saved in the "current directory".
So, I write a python script named reader.py
#!/usr/bin/env python
from __future__ import (print_function, absolute_import)
import os
import csv
import random
import string
cd_path = os.getcwd() # return path of current directory
filename = 'generic_file.csv' # filename 'test_enigma.csv'
filepath = os.path.join(os.getcwd(), filename) # returns path to open fname
print(filepath)
Now, if the user runs this in the terminal with python reader.py, it should output the name of the file, ONLY IF the file was saved in the current directory.
That's inconvenient. Most users will just download the file, and they would like reader.py to change to the subdirectory Downloads and read generic_file.csv from that directory.
(1) How does one use os.chdir() to work for every user?
(2) What is the standard way to do this if I was writing third-party software? I imagine I would have the user download the specific CSV file and Python script together.
If you are looking to get the path name of User A's download file, you can do os.path.expanduser('~/Downloads'). This will return /Users/A/Downloads

How to read a JSON static file in web2py

I'm putting together my first web2py app, and I've run into a bit of a problem. I have some data stored in static/mydata.json that I'd like to access in a couple places (specifically, in one of my models, as well as a module).
If this were a normal python script, obviously I'd do something like:
import json
with open('/path/to/mydata.json') as f:
mydata = json.load(f)
In the context of web2py, I can get the url of the file from URL('static', 'mydata.json'), but I'm not sure how to load mydata - can I just do mydata = json.load(URL('static','mydata.json')? Or is there another step required to open the file?
It's advisable to use os.path.join with request.folder to build paths to files.
import os
filepath = os.path.join(request.folder,'static','mydata.json')
From that point on, you should be able to use that filepath to open the json file as per usual.
import os
filepath = os.path.join(request.folder,'static','mydata.json')
mydata = json.load(filepath)

How do I decide where to put the file while creating it in python?

With the code open, I can create a new file in the computer. How do i decide which folder it goes? I need to put them in a certain folder when i am creating them. Should I put sth in the brackets?
e.g. open("apple juice. txt", "a")
If you don't specify a path, then the file will be created in the current directory. Where exactly that is depends on how you started the interpreter. For example, when you start Python 3.4 from the Windows Start Menu, then the file will be saved in C:\Python34\.
If you want to specify a certain path, then do so:
f = open(r"C:\Users\David\Python Files\apple juice.txt", "a")
Give the full path:
with open("path_where/to_save/apple_juice.txt", "a") as f:
# do work
with will automatically close your file.
If you are looking for a place for temp files, use the module tempfile.
You can use the function tempfile.gettempdir() to get a path to a folder directory.
You can use tempfile.TemporaryFile() to generate a full path to the place where the temp files are usually stored on your OS.
The method used in either case to generate the temp path is explained here.

Modify INI file with Python

I have an INI file I need to modify using Python. I was looking into the ConfigParser module but am still having trouble. My code goes like this:
config= ConfigParser.RawConfigParser()
config.read('C:\itb\itb\Webcams\AMCap1\amcap.ini')
config.set('Video','Path','C:\itb\itb')
But when looking at the amcap.ini file after running this code, it remains unmodified. Can anyone tell me what I am doing wrong?
ConfigParser does not automatically write back to the file on disk. Use the .write() method for that; it takes an open file object as it's argument.
config= ConfigParser.RawConfigParser()
config.read(r'C:\itb\itb\Webcams\AMCap1\amcap.ini')
config.set('Video','Path',r'C:\itb\itb')
with open(r'C:\itb\itb\Webcams\AMCap1\amcap.ini', 'wb') as configfile:
config.write(configfile)
You could use python-benedict, it's a dict subclass that provides normalized I/O support for most common formats, including ini.
from benedict import benedict
# path can be a ini string, a filepath or a remote url
path = 'path/to/config.ini'
d = benedict.from_ini(path)
# do stuff with your dict
# ...
# write it back to disk
d.to_ini(filepath=path)
It's well tested and documented, check the README to see all the features:
https://github.com/fabiocaccamo/python-benedict
Install using pip: pip install python-benedict
Note: I am the author of this project

Categories