Source code for utils

import json
import sys
import os
import random
import re
import string
from os.path import isfile, join


[docs]def confirmPath(path): """Confirm if a path is a valid directory path. Args: path (str): path to dir Raises: OSError: if path is not dir or does not exists Returns: string: the same path, if valid """ if not os.path.isdir(path): raise OSError( f'{path}: Path is not a directory path or it does not exists.') return path
[docs]def confirmFile(path): """Confirm if a path is a valid file path. Args: path (str): path to file Raises: OSError: if path is not file or does not exists Returns: str: the same path, if valid """ if not os.path.isfile(path): raise OSError( f'{path}: Path is not a file path or it does not exists.') return path
[docs]def loadConfig(): """Initialize the config.json file and config var Returns: dict: config dictionary """ # Side tweak to fix file path for testing (and running script and build docs) if not 'src' == os.path.basename(sys.path[0]): _localdir = os.path.join(sys.path[0], 'src') else: _localdir = sys.path[0] # if there is no config file (then it's .min), get the global config var for converter.min.py if not os.path.exists(os.path.join(_localdir, 'config.json')): global config else: # Load config.json with open(f"{os.path.join(_localdir, 'config.json')}") as json_data_file: config = json.load(json_data_file) return config
[docs]def loadPath(path=''): """Loads the PATH to the dir with the files to convert Args: path (str, optional): path of the dir (in config.json defaults to 'examples'). Defaults to ''. Returns: str: the path of the dir to convert the files """ # Doesn't need load for the config in main loads in the global scope # But it does have to put on the tests config = loadConfig() if path: _PATH = confirmPath(path) else: # Configure the PATH arg to run the script on if config["dirsSearch"]: _PATH = os.getcwd() + "/" + str(config["dirsSearch"][0]) + "/" else: _PATH = os.getcwd() + "/" return _PATH
[docs]def read(file): """Basic functions to read contents of files. Args: file (str): path of the file Returns: list: lines of the file """ if confirmFile(file): with open(file, "r") as f: lines = f.readlines() f.close() return lines
[docs]def write(root, new_name, lines): """Basic functions to write in a file. Args: root (str): path of the root for the file new_name (str): new name of the file lines (list): list of the string to write """ if confirmPath(root): with open(os.path.join(root, new_name), "w") as exf: exf.writelines(lines) exf.close()
[docs]def getVars(file): """Get and prepare the vars to use for a single file; Being the name, extension, new name and content (based on the proper regex) on the file. Args: file (str): path to the file Returns: tuple: (new_name, classes_file) - new_name (str): the new name with the minifier extension - classes_file (list): the content of the file extracted from the regex """ if confirmFile(file): # Get the extension and use as a flag e.g.: HTML, CSS... ext = os.path.splitext(file)[1].replace('.', '') filename = os.path.basename(file) onlyName = filename.split('.')[0] type_file = ext.upper() lines = read(file) # Use the right regex to find the classes on the file classes_file = re.findall(config["pattern" + type_file], str(lines)) if not '.min' in filename: new_name = onlyName + config["suffix"] + '.' + ext else: raise Exception(f'file: {filename} is a .min file') return new_name, classes_file
[docs]def lookFiles(path): """Look for the files to convert. Find all files with matching extensions out of config.json - Use _PATH as the root. - Ignore the dirs as specified in config['dirsIgnore'] - Select files with extensions as specified in config['filesSearch'] - Ignore files with extensions as specified in config['filesIgnore'] Args: path (str): path to convert the files Returns: list: list of tuples [(root, file)] of all the files founded (given the extension on config.json) """ _PATH = confirmPath(path) search_files = [] # Look for files for root, subdirectories, files in os.walk(_PATH): # Comprehension to break outer loop if any( [dirsIgnore for dirsIgnore in config["dirsIgnore"] if dirsIgnore in root] ): continue # And all its files for index, file in enumerate(files): for filesSearch in config["filesSearch"]: for filesIgnore in config["filesIgnore"]: if filesSearch in file and filesIgnore not in file: search_files.append((root, file)) return search_files
[docs]def getFiles(search_files, extension): """Get the list of files, separate and filter them by the extension and returns a tuple of (root, file) Args: search_files (list): list of all files searched extension (str): extension type Returns: list: list of tuples [(root, file)] of the selected extension - root (str): root path for the file (without the filename) - file (str): filename """ type_files = [ (root, file) for root, file in search_files if file.endswith(extension) ] return type_files
[docs]def cssHash(search_files): """Hash the css classes and return a tuple Args: search_files (list): list of all files founded Returns: tuple: (classes_dict, css_files, count) - classes_dict (list): list of dicts ({class: hashed}) of the hashed css classes - css_files (list): list of tuples ([(root, file)]) of css files - count (int): counter of the files altered """ classes_dict = {} count = 0 # Get all the files with the '.css' extension css_files = getFiles(search_files, ".css") # Copy the files of the search for root, file_name in css_files: lines = read(os.path.join(root, file_name)) new_name, classes = getVars(os.path.join(root, file_name)) # If the file doesn't exist, create a new one if config["overwriteFiles"] or not isfile(join(root, new_name)): # Create the dictionary with the classes hashes of the CSS for css_class in classes: name_hash_file = "-".join([file_name, css_class]) hash_number = "" for _ in range(config["hashLength"]): hash_number += str( "".join( random.choice( string.ascii_uppercase + string.ascii_lowercase + string.digits ) ) ) classes_dict.update({name_hash_file: hash_number}) # CSS WRITE # Copy the hashes to the copied lines of the file for index, single_line in enumerate(lines): for key, value in classes_dict.items(): key = key.split("-", 1)[1] if key in single_line: single_line = single_line.replace( key, config["prefix"] + value) if config["minimize"]: single_line = re.sub( config["patternCSSClear"], "", str(single_line) ).strip() lines[index] = "".join(single_line.split()) else: lines[index] = single_line write(root, new_name, lines) count += 1 if config["console"]: print(f"{10*'*'} \t {new_name.split('/')[-1]} \t {10*'*'}") # If it exists, pass else: print(f"!! file already existed: {new_name}") return classes_dict, css_files, count
[docs]def htmlHash(search_files, classes_dict, css_files): """Hash the html files and return a count of files altered Args: search_files (list): list of all files founded classes_dict (list): list of dicts ({class: hashed}) of the hashed css classes css_files (list): list of tuples ([(root, file)]) of css files Returns: int: count of the html files altered """ count = 0 # Overwrite HTML classes html_files = getFiles(search_files, ".html") # Copy the files of the search for root, file_name in html_files: lines = read(os.path.join(root, file_name)) new_name, substring = getVars(os.path.join(root, file_name)) # HTML WRITE # HTML overwrite link tags if config["overwriteFiles"] or not isfile(join(root, new_name)): css_found = set() # Search only the files (set) contained by the html for index, single_line in enumerate(lines): # look only the head lines - 'in' for minimized files if "</head>" in single_line: break for _, css_file_name in css_files: # if css_file_name in single_line and 'link' in single_line: if css_file_name in single_line: class_strip_name = re.findall( config["patternHTMLLinks"], str(single_line) )[0].split("/")[-1] css_found.add(class_strip_name) single_line = single_line.replace( css_file_name, f"{css_file_name.split('.')[0]}{config['suffix']}.css", ) lines[index] = single_line # Overwrite html lines with the hashed css classes for index, single_line in enumerate(lines): temp_dict = {} for key, value in classes_dict.items(): if key.split("-")[0] in css_found: temp_dict.update({key: value}) for key, value in temp_dict.items(): css_class = key.split("-", 1)[1] if css_class in single_line: single_line = single_line.replace( css_class, config["prefix"] + value ) if config["minimize"]: lines[index] = re.sub( config["patternHTMLClear"], "", str(single_line) ).strip() else: lines[index] = single_line write(root, new_name, lines) count += 1 if config["console"]: print(f"{10*'*'} \t {new_name.split('/')[-1]} \t {10*'*'}") # If it exists, pass else: print(f"!! file already existed: {new_name}") return count