Some user settings can be read and saved to a config file

This commit is contained in:
asdf 2019-11-30 23:00:37 +11:00
parent b736cd0b6e
commit e9df516ef5
3 changed files with 104 additions and 81 deletions

102
config.py
View File

@ -1,25 +1,93 @@
"""stores configuration settings for linkulator"""
import json
import stat
from pathlib import Path
from time import time
class DefaultConfigOptions:
"""data class for configuration options"""
class DefaultPaths:
"""data class containing configurable path defaults"""
all_homedir_pattern: str
datadir: str
datafile: str
ignorefile: str
my_datadir: Path
my_datafile: Path
my_ignorefile: Path
all_homedir_pattern: str = "/home/*/"
datadir: str = ".linkulator"
datafile: str = "linkulator.data"
ignorefile: str = "ignore"
settingsfile: str = "linkulatorrc"
CONFIG = DefaultConfigOptions()
CONFIG.all_homedir_pattern = "/home/*/"
CONFIG.datadir = ".linkulator"
CONFIG.datafile = "linkulator.data"
CONFIG.ignorefile = "ignore"
PATHS = DefaultPaths()
CONFIG.my_datadir = Path(Path.home() / CONFIG.datadir)
CONFIG.my_datafile = Path(CONFIG.my_datadir / CONFIG.datafile)
CONFIG.my_ignorefile = Path(CONFIG.my_datadir / CONFIG.ignorefile)
class DefaultUser(object):
"""data class containing user defaults"""
datadir: Path = Path.home() / PATHS.datadir
datafile: Path = datadir / PATHS.datafile
ignorefile: Path = datadir / PATHS.ignorefile
settingsfile: Path = datadir / PATHS.settingsfile
lastlogin = time()
browser: str = "lynx"
USER = DefaultUser()
def is_readable(st_mode: int) -> bool:
"""Checks the provided mode is group and other readable, returns true if this is the case
Check if 700 is readable:
>>> is_readable(16832)
False
Check if 755 is readable:
>>> is_readable(16877)
True
"""
if bool(st_mode & stat.S_IRGRP) & bool(st_mode & stat.S_IROTH):
return True
return False
def init():
"""Performs startup checks to ensure environment is set up for use
Creates necessary data directory and data file. If they exist, no error
occurs.
Checks that the data directory and data file are group and other readable.
Sets some correct permissions if they are not.
Other errors may raise an exception.
"""
USER.datadir.mkdir(mode=0o755, exist_ok=True)
if not is_readable(USER.datadir.stat().st_mode):
print(
"Warning: %s is not group or other readable - changing permissions"
% str(USER.datadir)
)
USER.datadir.chmod(0o755)
USER.datafile.touch(mode=0o644, exist_ok=True)
if not is_readable(USER.datafile.stat().st_mode):
print(
"Warning: %s is not group or other readable - changing permissions"
% str(USER.datafile)
)
USER.datafile.chmod(0o644)
try:
#TODO JSONDecodeError needs to be handled
with open(USER.settingsfile) as file:
l = json.load(file)
USER.lastlogin = l["lastlogin"]
USER.browser = l["browser"]
except FileNotFoundError:
with open(USER.settingsfile, 'w') as file:
json.dump({"lastlogin": USER.lastlogin, "browser": USER.browser}, file)
def save():
with open(USER.settingsfile, 'w') as file:
USER.lastlogin = str(time())
json.dump({"lastlogin": USER.lastlogin, "browser": USER.browser}, file)

View File

@ -5,7 +5,6 @@
import getpass
import os
import signal
import stat
import subprocess
import sys
import time
@ -14,13 +13,11 @@ from glob import glob
from pathlib import Path, PurePath
from shutil import which
import config
import posts
from config import CONFIG as config
username = getpass.getuser()
browser = "lynx"
help_text = """
options: -h or --help; -p or --post; or no option to browse links.
@ -55,8 +52,8 @@ ignore_names = []
# IGNORE NAMES.
def parse_ignore_file():
global ignore_names
if config.my_ignorefile.exists():
s = config.my_ignorefile.read_text()
if config.USER.ignorefile.exists():
s = config.USER.ignorefile.read_text()
l = s.splitlines()
for line in l:
name = line.split(" ")[0]
@ -68,7 +65,9 @@ def build_menu():
## DISK IO IS PROBABLY THE HEAVIEST PART OF THIS SCRIPT, DON'T DO THIS OFTEN.
files_pattern = str(
PurePath(config.all_homedir_pattern).joinpath(config.datadir, config.datafile)
PurePath(config.PATHS.all_homedir_pattern).joinpath(
config.PATHS.datadir, config.PATHS.datafile
)
)
linkulator_files = glob(files_pattern)
@ -223,7 +222,7 @@ def view_thread(post_id):
next_text = "\nType {} to reply, {} to view in {}, {} for main menu, or {} to quit: ".format(
style_text("r", "underline"),
style_text("b", "underline"),
browser,
config.USER.browser,
style_text("m", "underline"),
style_text("q", "underline"),
)
@ -246,10 +245,10 @@ def view_thread(post_id):
def view_link_in_browser(url, post_id):
if which(browser) is None:
if which(config.USER.browser) is None:
print(
"Sorry, "
+ browser
+ config.USER.browser
+ " is not installed on your system. Ask your sysadmin to install it."
)
view_thread(post_id)
@ -259,12 +258,12 @@ def view_link_in_browser(url, post_id):
or url.startswith("https://")
or url.startswith("http://")
):
subprocess.call(["lynx", url])
subprocess.call([config.USER.browser, url])
else:
print("Sorry, that url doesn't start with gopher://, http:// or https://")
tryAnyway = input("Do you want to try it in", browser, "anyway? Y/[N] ")
tryAnyway = input("Do you want to try it in", config.USER.browser, "anyway? Y/[N] ")
if tryAnyway == "Y" or tryAnyway == "y":
subprocess.call(["lynx", url])
subprocess.call([config.USER.browser, url])
view_thread(post_id)
@ -273,11 +272,11 @@ def reply(owner, tstamp, post_id):
comment = input("Enter your comment: ")
if os.path.exists(config.my_datafile):
if os.path.exists(config.USER.datafile):
append_write = "a" # append if already exists
else:
append_write = "w+" # make a new file if not
with open(config.my_datafile, append_write) as file:
with open(config.USER.datafile, append_write) as file:
timestamp = str(time.time())
file.write(timestamp + "|" + owner + "+" + tstamp + "|||" + comment + "\r")
@ -307,53 +306,9 @@ def search(keyword):
## if next_step...
def is_readable(st_mode: int) -> bool:
"""Checks the provided mode is group and other readable, returns true if this is the case
Check if 700 is readable:
>>> is_readable(16832)
False
Check if 755 is readable:
>>> is_readable(16877)
True
"""
if bool(st_mode & stat.S_IRGRP) & bool(st_mode & stat.S_IROTH):
return True
return False
def init():
"""Performs startup checks to ensure environment is set up for use
Creates necessary data directory and data file. If they exist, no error
occurs.
Checks that the data directory and data file are group and other readable.
Sets some correct permissions if they are not.
Other errors may raise an exception.
"""
dir_p = Path(Path.home(), ".linkulator")
file_p = Path(dir_p, "linkulator.data")
dir_p.mkdir(mode=0o755, exist_ok=True)
file_p.touch(mode=0o644, exist_ok=True)
if not is_readable(dir_p.stat().st_mode):
print(
"Warning: %s is not group or other readable - changing permissions"
% str(dir_p)
)
dir_p.chmod(0o755)
if not is_readable(file_p.stat().st_mode):
print(
"Warning: %s is not group or other readable - changing permissions"
% str(file_p)
)
file_p.chmod(0o644)
def graceful_exit():
print("\n\nThank you for linkulating. Goodbye.\n")
config.save()
exit(0)
@ -366,7 +321,7 @@ signal.signal(signal.SIGINT, signal_handler)
def parse_command():
args = sys.argv[1:]
init()
config.init()
if not len(args):
print(" ----------")
print(" LINKULATOR")

View File

@ -6,7 +6,7 @@ import readline
import sys
import time
from config import CONFIG as config
import config
USERNAME = getpass.getuser()
@ -57,11 +57,11 @@ def get_input(item: str, prefill) -> str:
def save_link(link) -> None:
"""Saves the specified link data to the user's data file"""
if os.path.exists(config.my_datafile):
if os.path.exists(config.USER.datafile):
append_write = "a" # append if already exists
else:
append_write = "w+" # make a new file if not
with open(config.my_datafile, append_write) as file:
with open(config.USER.datafile, append_write) as file:
file.write(
link.timestamp
+ "||"