diff --git a/.github/ISSUE_TEMPLATE/new-feature.md b/.github/ISSUE_TEMPLATE/new-feature.md new file mode 100644 index 0000000..d62129b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/new-feature.md @@ -0,0 +1,17 @@ +--- +name: New Feature +about: Task for team members to complete +title: '' +labels: '' +assignees: '' + +--- + +**What is the task** +A clear and concise description of what the task is + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/cspell.json b/.github/cspell.json index eb351a2..9f91e0b 100644 --- a/.github/cspell.json +++ b/.github/cspell.json @@ -13,7 +13,15 @@ "virtualenvs", "git", "gitignore", - "github" + "github", + "favou", + "pwsh", + "fastfail", + "gatorconfig", + "gatorgrader" + + + ], "ignorePaths": [ ".github/**", diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5ceb500..de2c03e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -71,5 +71,9 @@ jobs: poetry env info - name: Install dependencies run: poetry install --no-interaction --no-ansi + - name: setup ${{ matrix.os }} + run: | + sudo apt install libxkbcommon-x11-0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0 libxcb-xfixes0 + /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 1920x1200x24 -ac +extension GLX - name: Execute tests run: poetry run task test diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..7d6c6a0 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,7 @@ +[MASTER] + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code. (This is an alternative name to extension-pkg-allow-list +# for backward compatibility.) +extension-pkg-whitelist=PyQt5 diff --git a/README.md b/README.md index bbb290a..314cfba 100644 --- a/README.md +++ b/README.md @@ -1 +1,59 @@ -# project-team-4 + +# team4-GatorConfig + +A simple Python project utilizing a CLI approach to automate generating +configuration files for GatorGrader. The GitHub Actions workflow executes +[pytest](https://pytest.org/) (with +[coverage](https://pypi.org/project/pytest-cov/)) and +[pylint](https://pylint.org/) using the Poetry configuration, and checks +markdown with [markdownlint](https://github.com/DavidAnson/markdownlint) and +spelling with [cspell](https://cspell.org/). + +## Requirements + +- [Python](https://realpython.com/installing-python/) +- [Pipx](https://pypa.github.io/pipx/installation/) +- [Poetry](https://python-poetry.org/docs/#installing-with-pipx) + +## Usage + +### Installing Python dependencies + +After cloning this project, you will likely want to instruct Poetry to create a +virtual environment and install the Python packages (like pytest and pylint) +listed in `pyproject.toml`. + +To install Python dependencies: + +```bash +poetry install +``` + +### Running tasks + +This project uses the [taskipy](https://github.com/illBeRoy/taskipy) task runner +to simplify testing and linting. You can see the actual commands run when tasks +are executed under the `[tool.taskipy.tasks]` header in `pyproject.toml`. + +- **Test** your code with `poetry run task test` +- **Lint** your code with `poetry run task lint` + +### Running GatorConfig + +GatorConfig is a tool that will utilize the command line interface, which +was built to accommodate the users. To run the GatorConfig program +in CLI, type the command: + +`poetry run gatorconfig` + +Once you run this command, the program will output + +`Wrote file to: C:\Users\\GatorConfig\gatorgrader.yml` + +This command will auto-generate a default configuration file for GatorGradle +named `gatorgrader.yml` which will contain a default input for +the variables, such as the name, break, fastfail, etc., + +Additionally, you can run the `poetry run gatorconfig --help` for more +information about the configuration. This command will list out the variables +in the file as well as the defaults it outputs. diff --git a/hello/__init__.py b/gatorconfig/__init__.py similarity index 100% rename from hello/__init__.py rename to gatorconfig/__init__.py diff --git a/gatorconfig/actions_configuration.py b/gatorconfig/actions_configuration.py new file mode 100644 index 0000000..8add95d --- /dev/null +++ b/gatorconfig/actions_configuration.py @@ -0,0 +1,14 @@ +"""This module generates a GitHub Actions configuration file that uses GatorGradle""" +from pathlib import Path +from ruamel.yaml import YAML + +def create_configuration_file(target_file): + """Create the workflow configuration file itself""" + default = {'name': 'Grade', 'on': ['push', 'pull_request'], 'jobs': + {'grade': {'runs-on': 'ubuntu-latest', 'steps': + [{'name': 'Checkout repository', 'uses': 'actions/checkout@v2'}, + {'name': 'Run GatorGradle', 'uses': 'GatorEducator/gatorgradle-action@v1'}]}}} + yaml = YAML() + yaml.default_flow_style = False + out = Path(target_file) + yaml.dump(default, out) diff --git a/gatorconfig/gator_config.py b/gatorconfig/gator_config.py new file mode 100644 index 0000000..39c7ed4 --- /dev/null +++ b/gatorconfig/gator_config.py @@ -0,0 +1,92 @@ +"""Capture user input to automatically generate YAML file.""" +from typing import Dict +from typing import List +from pathlib import Path +import typer +from gatorconfig import gator_yaml +from gatorconfig import actions_configuration + +cli = typer.Typer() + + + +#pylint: disable=too-many-arguments +@cli.command() +def cli_input( + name: str = typer.Option("Project"), + brk: bool = typer.Option(False, "--break"), + fastfail: bool = typer.Option(False), + gen_readme: bool = typer.Option(False), + file: List[str] = typer.Option([]), + #language: str = typer.Option(None), + output_path: Path = typer.Option(Path.cwd()), + indent: int = typer.Option(4), + commit_count: int = typer.Option(5) +): + """Gather input from the command line. + + Args: + name (str, optional): [description]. Defaults to typer.Option("Project"). + brk (bool, optional): [description]. Defaults to typer.Option(False, "--break"). + fastfail (bool, optional): [description]. Defaults to typer.Option(False). + file (List[str], optional): [description]. Defaults to typer.Option([]). + indent (int, optional): [description]. Defaults to typer.Option(4). + commit_count (int, optional): [description]. Defaults to typer.Option(5). + """ + files = get_checks(file) + + yaml_out = gator_yaml.GatorYaml() + #print(files) + # Creation of the output variable + output = { + "name": name, + "break": brk, + "fastfail": fastfail, + "readme": gen_readme, + "indent": indent, + "commits": commit_count, + "files": files + } + file_yaml = yaml_out.dump(output, paths=output["files"]) + output_file(file_yaml, output_path) + actions_configuration.create_configuration_file('../.github/workflows/grade.yml') + + +def output_file(yaml_string: str, output_path: Path): + """Create and write to file if it doesn't exist, writes to file otherwise. + + Args: + yaml_string (str): [description] + output_path (Path): [description] + """ + fle = Path(output_path / "gatorgrader.yml") + fle.touch(exist_ok=True) + with open(fle, "w", encoding="utf8") as yml: + yml.write(yaml_string) + print(f"Wrote file to: {fle}") + +def get_checks(file: List[Path]) -> Dict: + """Read in checks per file. + + Args: + file (List[Path]): List of file paths read in from command line. + + Returns: + Dict: Dictionary of file paths and checks to perform per file. + """ + files = {} + for item in file: + running = True + print("") + check_list = [] + while running: + check = input(f"Enter a check for {item} (Press \"Enter\" to move on): ") + if check.lower() == "": + running = False + else: + check_list.append(check) + files[item] = check_list + return files + +if __name__ == "__main__": + cli() diff --git a/gatorconfig/gator_yaml.py b/gatorconfig/gator_yaml.py new file mode 100644 index 0000000..006b7c1 --- /dev/null +++ b/gatorconfig/gator_yaml.py @@ -0,0 +1,106 @@ +"""Converts dictionaries to GatorYAML""" +from gatorconfig.split_file_path import split_file_path + + +class GatorYaml: + """Main GatorYaml object""" + + def __init__(self, indent=4, spaces=4): + """Init GatorYAML object. Takes optional arguments to change indent of files + and how many spaces is considered a tab """ + self.spaces = spaces # How many spaces is a tab + self.tabs = -1 # Current tab level + self.output = "" # Init output + self.keywords = ["(pure)", "commits"] # Any keywords to look for + self.indents = indent # set indent for file path + + def dump(self, dic, paths=None): + """Input dictionary is parsed. returns string of valid YAML""" + + if paths is not None: + if isinstance(paths, dict): + dic["files"] = split_file_path(paths) + else: + raise Exception("Paths expected to be \"dict\", got " + str(type(paths)) + "!") + + self.enum_dict(dic) + + return self.output + + def enum_list(self, list_in): + """Enumerate through input list and output each item unless it finds another dictionary.""" + for i in list_in: + if isinstance(i, dict): + self.tabs += 1 + self.enum_dict(i) + else: + self.output_list_item(i) + + def enum_dict(self, dic): + """Enumerate through input dictionary and output each key""" + self.tabs += 1 + + for k in dic.keys(): + if k == "indent": + self.indents = int(dic[k]) + + if k == "files": + self.tabs -= 1 + # if isinstance(d[k], list): + # self.enum_list(d[k]) + if isinstance(dic[k], dict): + self.enum_file_dict(dic[k]) + elif isinstance(dic[k], list): + self.output_key(k) + self.enum_list(dic[k]) + elif isinstance(dic[k], dict): + self.output_key(k) + self.enum_dict(dic[k]) + else: + if not self.is_keyword(k, dic[k]): + self.output_key_value(k, dic[k]) + + self.tabs -= 1 + + def enum_file_dict(self, files): + """Enumerate through the file list dictionary and output each key""" + self.tabs += 1 + + for k in files: + if isinstance(files[k], dict): + self.output_key(k) + self.enum_file_dict(files[k]) + elif isinstance(files[k], list): + self.output_key(k) + self.enum_file_list(files[k]) + + self.tabs -= 1 + + def enum_file_list(self, list_in): + """Enumerate through each file key's parameter list items""" + for item in list_in: + self.output += (" " * self.spaces) * self.indents + str(item) + "\n" + + def output_list_item(self, item): + """Output a generic list item""" + self.output += (" " * self.spaces) * self.tabs + " -" + str(item) + "\n" + + def output_key(self, key): + """Output a generic key""" + self.output += (" " * self.spaces) * self.tabs + str(key) + ":\n" + + def output_key_value(self, key, value): + """Output a generic key and it's value""" + self.output += ((" " * self.spaces) * self.tabs) + str(key) + ": " + str(value) + "\n" + + def is_keyword(self, key, value): + """Output key and value if a keyword""" + if key in self.keywords: + if key == "commits": + self.output += (" " * self.spaces) * self.tabs \ + + "--" + str(key) + " " + str(value) + "\n" + else: + self.output += (" " * self.spaces) * self.tabs + str(key) \ + + " " + str(value) + "\n" + return True + return False diff --git a/gatorconfig/gui/check_file.py b/gatorconfig/gui/check_file.py new file mode 100644 index 0000000..a2c37f2 --- /dev/null +++ b/gatorconfig/gui/check_file.py @@ -0,0 +1,59 @@ +"""Checked File widgets""" + +import os +from PyQt5.QtWidgets import QWidget, \ + QVBoxLayout, QLabel, \ + QHBoxLayout, QLineEdit, QPushButton, \ + QPlainTextEdit, QFrame, \ + QFileDialog + + +class CheckFile(QWidget): + """QWidget class for a checked file""" + + def __init__(self, parent=None): + """Init QWidget parent and the widgets that make up a checked file widget""" + super().__init__(parent) + self.outer_lay = QVBoxLayout() + + self.file = None + + # ----File Browser----# + # Hbox Layout + lay = QHBoxLayout() + self.check_label = QLabel("File ") + self.path_text = QLineEdit() + self.path_button = QPushButton("Open", + clicked=lambda: self.get_path_from_file(self.path_text)) + self.file_params = QPlainTextEdit() + + # Add widgets to hbox layout + lay.addWidget(self.check_label) + lay.addWidget(self.path_text) + lay.addWidget(self.path_button) + + self.outer_lay.addLayout(lay) + self.outer_lay.addWidget(self.file_params) + + # Horizontal line separator + self.frame = QFrame() + self.frame.setFrameShape(QFrame.HLine) + self.frame.setFrameShadow(QFrame.Sunken) + + self.outer_lay.addWidget(self.frame) + + self.setLayout(self.outer_lay) + + def get_path_from_file(self, target_text): + """Opens a file dialog to select a new file. + Sets self.file to the file name and sets the tab text.""" + self.file = QFileDialog.getOpenFileName(self, 'OpenFile')[0] + target_text.setText(self.file) + tabs = self.parentWidget().parentWidget() + tabs.setTabText(tabs.currentIndex(), os.path.basename(self.file)) + + def get_data(self): + """Gets all the data of the checked files and returns their file paths and params.""" + if self.file is None: + self.file = self.path_text.text() + return self.file, self.file_params.toPlainText().split() diff --git a/gatorconfig/gui/form.py b/gatorconfig/gui/form.py new file mode 100644 index 0000000..f18978d --- /dev/null +++ b/gatorconfig/gui/form.py @@ -0,0 +1,148 @@ +"""Form that makes up the configuration GUI""" +from PyQt5.QtWidgets import QTabWidget, \ + QWidget, QFormLayout, \ + QCheckBox, QLineEdit, \ + QVBoxLayout, QPushButton, \ + QSpinBox, QHBoxLayout, QPlainTextEdit +from gatorconfig.gui.check_file import CheckFile + +# pylint: disable=R0902 +# pylint: disable=W0108 +# Disabling R0902 because this is a main class. PyQt5 *requires* this type of code. +# Could split this into multiple classes if REALLY needed but that can wait until later. +# Also disabling W0108 because the lambda is necessary. + +class Form(QTabWidget): + """Main form class, contains basic and advanced tabs.""" + def __init__(self, parent=None): + """Init the form. Create the two tabs and populate them with widgets.""" + super().__init__(parent) + + # Create tabs + self.basic = QWidget() + self.advanced = QWidget() + + # Add form to tabs + self.addTab(self.basic, "Basic") + self.addTab(self.advanced, "Advanced") + self.basic_tab() + self.advanced_tab() + + def basic_tab(self): + """Creates a new form and populates it with the widgets for basic configuration""" + # Create a QFormLayout instance + form_layout = QFormLayout() + + # Add widgets to the form + self.fast_fail = QCheckBox() + self.break_fail = QCheckBox() + self.generate_readme = QCheckBox() + + self.grader_version = QLineEdit("v0.2.0") + + self.assignment_name = QLineEdit("Default") + + form_layout.addRow("Fast Fail:", self.fast_fail) + form_layout.addRow("Break:", self.break_fail) + form_layout.addRow("Generate ReadMe:", self.generate_readme) + form_layout.addRow("GatorGrader Version:", self.grader_version) + form_layout.addRow("Assignment Name:", self.assignment_name) + + # Create VBox for group + group_lay = QVBoxLayout() + + self.tabs = QTabWidget() + self.tabs.setTabsClosable(True) + self.tabs.tabCloseRequested.connect(self.tabs.removeTab) + + # Checked Files Controls + # Create add file button + add_file_button = QPushButton( + "Add New Checked File", clicked=lambda: self.add_checked_file()) + + # Add button to layout + group_lay.addWidget(add_file_button) + + # group.setLayout(group_lay) + form_layout.addRow(self.tabs) + form_layout.addRow(group_lay) + + self.setTabText(0, "Basic") + self.basic.setLayout(form_layout) + + def advanced_tab(self): + """Creates the form for the advanced tab and populates it with the required widgets.""" + # Create a QFormLayout instance + form_layout = QFormLayout() + + # Add widgets to the form + + # ---Indent Size---# + self.indent_size = QSpinBox() + self.indent_size.setValue(4) + self.indent_size.setMinimum(1) + + form_layout.addRow("Indent Size:", self.indent_size) + + # ---Startup Script---# + + # Hbox Layout + self.lay = QHBoxLayout() + self.startup_script_text = QLineEdit() + self.startup_script_button = QPushButton("Open", + clicked=lambda: self.get_path_from_file( + self.startup_script_text, + self.startup_script_button)) + + # connect button to file dialog + # self.startup_script_button.clicked.connect() + + # Add widgets to hbox layout + self.lay.addWidget(self.startup_script_text) + self.lay.addWidget(self.startup_script_button) + + form_layout.addRow("Startup Script:", self.lay) + + self.description_input2 = QPlainTextEdit() + + # submit_button = QPushButton("Submit", clicked = lambda: self.submit_clicked()) + # submit_button.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + + self.setTabText(1, "Advanced") + self.advanced.setLayout(form_layout) + + def add_checked_file(self): + """Creates a new CheckFile and adds it to the basic config's file tabs.""" + self.tabs.addTab(CheckFile(), "File " + str(self.tabs.count() + 1)) + self.tabs.setCurrentIndex(self.tabs.currentIndex() + 1) + + # group_lay.insertWidget(group_lay.count() - 1, CheckFile(group_lay.count())) + + def change_tab_text(self, text): + """Changes the current tab's text.""" + self.tabs.setTabText(self.tabs.currentIndex(), text) + + def submit_form(self): + """Submits the data from both the basic and advanced tabs.""" + files = {} + + for index in range(self.tabs.count()): + widget = self.tabs.widget(index) + key, val = widget.get_data() + files[key] = val + + full_data = {"name": self.assignment_name.text(), + "break": self.break_fail.isChecked(), + "fastfail": self.fast_fail.isChecked(), + "indent": int(self.indent_size.text()), + "idcommand": "", + "version": self.grader_version.text(), + "executables": "", + "startup": self.startup_script_text.text(), + "reflection": "", + "files": files + } + + print("Form Submitted!") + # print(full_data) + return full_data diff --git a/gatorconfig/gui/homepage.py b/gatorconfig/gui/homepage.py new file mode 100644 index 0000000..8e0813d --- /dev/null +++ b/gatorconfig/gui/homepage.py @@ -0,0 +1,48 @@ +"""Main page for the PyQT app""" + +from PyQt5.QtCore import Qt +from PyQt5.QtWidgets import QMainWindow, QWidget, QVBoxLayout, QLabel, QPushButton +from gatorconfig.gui.form import Form + + +class Homepage(QMainWindow): + """Custom QMainWindow object""" + def __init__(self, owner=None, parent=None): + """Init the widget, populate it with the configuration form""" + super().__init__(parent) + self.setWindowTitle("New Configuration") + + self.owner = owner + # Create Central Widget + main_win = QWidget() + + # Create Layout + lay = QVBoxLayout() + + # Create top label + top_label = QLabel("New Configuration File", alignment=Qt.AlignHCenter) + + # Create Form + form = Form() + + # Create Submit Button + submit_button = QPushButton("Submit", clicked=lambda: self.submit_form(form)) + + # Add widgets + lay.addWidget(top_label) + lay.addWidget(form) + lay.addWidget(submit_button) + lay.setAlignment(submit_button, Qt.AlignRight) + lay.addStretch() + + # Set Layout + main_win.setLayout(lay) + + # Set central widget + self.setCentralWidget(main_win) + + def submit_form(self, form): + """Gathers data from the form and closes the application""" + data = form.submit_form() + self.owner.data = data + self.close() diff --git a/gatorconfig/gui_main.py b/gatorconfig/gui_main.py new file mode 100644 index 0000000..ca320b7 --- /dev/null +++ b/gatorconfig/gui_main.py @@ -0,0 +1,51 @@ +"""A GUI to easily create GatorYAML configuration dictionaries""" +import sys + +from PyQt5.QtWidgets import QApplication + +from gatorconfig.gui.homepage import Homepage + + +# from Form import Form +# from Homepage import Homepage + +class Gui: + """Main GUI object""" + + def __init__(self): + """Initializes PyQT application""" + # Create App + self.app = QApplication(sys.argv) + + # Open the qss styles file and read + # with open('./style.qss', 'r') as f: + # style = f.read() + # + # # Set the stylesheet of the application + # app.setStyleSheet(style) + # print(QStyleFactory.keys()) + + # Make it look not as bad... + # app.setStyle() + + self.data = None + + # Show Window + self.win = Homepage(owner=self) + self.win.setGeometry(400, 400, 400, 300) + + self.win.show() + + self.app.exec_() + + + def close_window(self): + """Closes the window. Added to appease lord PyLint""" + self.win.close() + + def get_data(self): + """Returns submitted form data""" + return self.data + +# gui = Gui() +# print(type(gui)) diff --git a/gatorconfig/scrape_releases.py b/gatorconfig/scrape_releases.py new file mode 100644 index 0000000..7e173bd --- /dev/null +++ b/gatorconfig/scrape_releases.py @@ -0,0 +1,32 @@ +"""Gets list of releases from GitHub""" +import re +from typing import List +import requests + + +def get_github_releases(url: str) -> List: + """Takes a URL or organization and repo and returns a list of all releases.""" + output = [] + + if "/releases" in url: + response = requests.get(url).json() + elif "https://github.com/" in url: + repo = url.replace("https://github.com/", "") + response = requests.get(f"https://api.github.com/repos/{repo}/releases").json() + else: + pat = re.compile("https://api.github.com/repos/(.*)/releases") + match = pat.search(url) + + if match is None: + data = url.split("/") + + if len(data) != 2: + raise Exception("Could not parse " + url) + else: + data = match.group(1).split("/") + + response = requests.get(f"https://api.github.com/repos/{data[0]}/{data[1]}/releases").json() + + for item in response: + output.append(item["name"]) + return output diff --git a/gatorconfig/split_file_path.py b/gatorconfig/split_file_path.py new file mode 100644 index 0000000..ef3595d --- /dev/null +++ b/gatorconfig/split_file_path.py @@ -0,0 +1,25 @@ +"""Split file path module.""" + + +def split_file_path(paths: list) -> dict: + """Convert files paths stored in a dict to nested dicts.""" + output = {} + for key, value in paths.items(): + directories = key.split('/') + dir_dic = output + for direc in directories[:-1]: + if direc not in dir_dic: + dir_dic[direc] = {} + dir_dic = dir_dic[direc] + dir_dic[directories[-1]] = value + + return output + +# example dictionary containing three file paths +# path_dic = { +# 'gatorconfig/main/java/samplelab/SampleLabMain.java': [''], +# 'gatorconfig/main/java/samplelab/DataClass.java': [''], +# 'writing/reflection.md': [''] +# } +# final_dic = split_file_path(path_dic) +# print(final_dic) diff --git a/hello/say_hello.py b/hello/say_hello.py deleted file mode 100644 index 43ac79e..0000000 --- a/hello/say_hello.py +++ /dev/null @@ -1,10 +0,0 @@ -"""This module implements the ability to greet users by name.""" - -def greet(name): - """Greet a user by name.""" - greeting = f"Hello, {name}!" - print(greeting) - - -if __name__ == "__main__": - greet("World") diff --git a/poetry.lock b/poetry.lock index 1af2337..88d57dc 100644 --- a/poetry.lock +++ b/poetry.lock @@ -34,21 +34,52 @@ docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] +[[package]] +name = "certifi" +version = "2021.10.8" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "charset-normalizer" +version = "2.0.10" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.5.0" + +[package.extras] +unicode_backport = ["unicodedata2"] + +[[package]] +name = "click" +version = "8.0.3" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + [[package]] name = "colorama" version = "0.4.4" description = "Cross-platform colored terminal text." -category = "dev" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "coverage" -version = "6.2" +version = "6.3" description = "Code coverage measurement for Python" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] tomli = {version = "*", optional = true, markers = "extra == \"toml\""} @@ -56,11 +87,27 @@ tomli = {version = "*", optional = true, markers = "extra == \"toml\""} [package.extras] toml = ["tomli"] +[[package]] +name = "easyprocess" +version = "1.1" +description = "Easy to use Python subprocess interface." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "idna" +version = "3.3" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" + [[package]] name = "importlib-metadata" version = "4.10.1" description = "Read metadata from Python packages" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" @@ -111,6 +158,19 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "mock" +version = "4.0.3" +description = "Rolling backport of unittest.mock for all Pythons" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +build = ["twine", "wheel", "blurb"] +docs = ["sphinx"] +test = ["pytest (<5.4)", "pytest-cov"] + [[package]] name = "mslex" version = "0.3.0" @@ -195,7 +255,7 @@ typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\"" [[package]] name = "pyparsing" -version = "3.0.6" +version = "3.0.7" description = "Python parsing module" category = "dev" optional = false @@ -204,6 +264,34 @@ python-versions = ">=3.6" [package.extras] diagrams = ["jinja2", "railroad-diagrams"] +[[package]] +name = "pyqt5" +version = "5.15.6" +description = "Python bindings for the Qt cross platform application toolkit" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +PyQt5-Qt5 = ">=5.15.2" +PyQt5-sip = ">=12.8,<13" + +[[package]] +name = "pyqt5-qt5" +version = "5.15.2" +description = "The subset of a Qt installation needed by PyQt5." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "pyqt5-sip" +version = "12.9.0" +description = "The sip module support for PyQt5" +category = "main" +optional = false +python-versions = ">=3.5" + [[package]] name = "pytest" version = "6.2.5" @@ -241,6 +329,123 @@ pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] +[[package]] +name = "pytest-mock" +version = "3.6.1" +description = "Thin-wrapper around the mock package for easier use with pytest" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pytest = ">=5.0" + +[package.extras] +dev = ["pre-commit", "tox", "pytest-asyncio"] + +[[package]] +name = "pytest-qt" +version = "4.0.2" +description = "pytest support for PyQt and PySide applications" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pytest = ">=3.0.0" + +[package.extras] +dev = ["pre-commit", "tox"] +doc = ["sphinx", "sphinx-rtd-theme"] + +[[package]] +name = "pytest-xvfb" +version = "2.0.0" +description = "A pytest plugin to run Xvfb for tests." +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +pytest = ">=2.8.1" +pyvirtualdisplay = ">=1.3" + +[[package]] +name = "pyvirtualdisplay" +version = "2.2" +description = "python wrapper for Xvfb, Xephyr and Xvnc" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +EasyProcess = "*" + +[[package]] +name = "requests" +version = "2.27.1" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} +idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] + +[[package]] +name = "requests-mock" +version = "1.9.3" +description = "Mock out responses from the requests package" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +requests = ">=2.3,<3" +six = "*" + +[package.extras] +fixture = ["fixtures"] +test = ["fixtures", "mock", "purl", "pytest", "sphinx", "testrepository (>=0.0.18)", "testtools"] + +[[package]] +name = "ruamel.yaml" +version = "0.17.20" +description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" +category = "main" +optional = false +python-versions = ">=3" + +[package.dependencies] +"ruamel.yaml.clib" = {version = ">=0.2.6", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""} + +[package.extras] +docs = ["ryd"] +jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] + +[[package]] +name = "ruamel.yaml.clib" +version = "0.2.6" +description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + [[package]] name = "taskipy" version = "1.9.0" @@ -273,20 +478,50 @@ python-versions = ">=3.7" [[package]] name = "typed-ast" -version = "1.5.1" +version = "1.5.2" description = "a fork of Python 2 and 3 ast modules with type comment support" category = "dev" optional = false python-versions = ">=3.6" +[[package]] +name = "typer" +version = "0.4.0" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +click = ">=7.1.1,<9.0.0" + +[package.extras] +all = ["colorama (>=0.4.3,<0.5.0)", "shellingham (>=1.3.0,<2.0.0)"] +dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)"] +doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=5.4.0,<6.0.0)", "markdown-include (>=0.5.1,<0.6.0)"] +test = ["shellingham (>=1.3.0,<2.0.0)", "pytest (>=4.4.0,<5.4.0)", "pytest-cov (>=2.10.0,<3.0.0)", "coverage (>=5.2,<6.0)", "pytest-xdist (>=1.32.0,<2.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "mypy (==0.910)", "black (>=19.10b0,<20.0b0)", "isort (>=5.0.6,<6.0.0)"] + [[package]] name = "typing-extensions" version = "4.0.1" description = "Backported and Experimental Type Hints for Python 3.6+" -category = "dev" +category = "main" optional = false python-versions = ">=3.6" +[[package]] +name = "urllib3" +version = "1.26.8" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" + +[package.extras] +brotli = ["brotlipy (>=0.6.0)"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + [[package]] name = "wrapt" version = "1.13.3" @@ -299,7 +534,7 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" name = "zipp" version = "3.7.0" description = "Backport of pathlib-compatible object wrapper for zip files" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" @@ -310,7 +545,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest- [metadata] lock-version = "1.1" python-versions = ">=3.7,<4.0" -content-hash = "ee58477130d28306682a1e0f3ef8ffd4e4436155980456d978361a4e666e1236" +content-hash = "dfb054dd852b217e80939702e9b974da0f4df54340683e59e70e58281f5fdef9" [metadata.files] astroid = [ @@ -325,58 +560,75 @@ attrs = [ {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, ] +certifi = [ + {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, + {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, +] +charset-normalizer = [ + {file = "charset-normalizer-2.0.10.tar.gz", hash = "sha256:876d180e9d7432c5d1dfd4c5d26b72f099d503e8fcc0feb7532c9289be60fcbd"}, + {file = "charset_normalizer-2.0.10-py3-none-any.whl", hash = "sha256:cb957888737fc0bbcd78e3df769addb41fd1ff8cf950dc9e7ad7793f1bf44455"}, +] +click = [ + {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, + {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}, +] colorama = [ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] coverage = [ - {file = "coverage-6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b"}, - {file = "coverage-6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0"}, - {file = "coverage-6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da"}, - {file = "coverage-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d"}, - {file = "coverage-6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739"}, - {file = "coverage-6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971"}, - {file = "coverage-6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840"}, - {file = "coverage-6.2-cp310-cp310-win32.whl", hash = "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c"}, - {file = "coverage-6.2-cp310-cp310-win_amd64.whl", hash = "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f"}, - {file = "coverage-6.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76"}, - {file = "coverage-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47"}, - {file = "coverage-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64"}, - {file = "coverage-6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9"}, - {file = "coverage-6.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d"}, - {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48"}, - {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e"}, - {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d"}, - {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17"}, - {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781"}, - {file = "coverage-6.2-cp36-cp36m-win32.whl", hash = "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a"}, - {file = "coverage-6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0"}, - {file = "coverage-6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49"}, - {file = "coverage-6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521"}, - {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884"}, - {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa"}, - {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64"}, - {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617"}, - {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8"}, - {file = "coverage-6.2-cp37-cp37m-win32.whl", hash = "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4"}, - {file = "coverage-6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74"}, - {file = "coverage-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e"}, - {file = "coverage-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58"}, - {file = "coverage-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc"}, - {file = "coverage-6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd"}, - {file = "coverage-6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953"}, - {file = "coverage-6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475"}, - {file = "coverage-6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57"}, - {file = "coverage-6.2-cp38-cp38-win32.whl", hash = "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c"}, - {file = "coverage-6.2-cp38-cp38-win_amd64.whl", hash = "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2"}, - {file = "coverage-6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd"}, - {file = "coverage-6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685"}, - {file = "coverage-6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c"}, - {file = "coverage-6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3"}, - {file = "coverage-6.2-cp39-cp39-win32.whl", hash = "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282"}, - {file = "coverage-6.2-cp39-cp39-win_amd64.whl", hash = "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644"}, - {file = "coverage-6.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de"}, - {file = "coverage-6.2.tar.gz", hash = "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8"}, + {file = "coverage-6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e8071e7d9ba9f457fc674afc3de054450be2c9b195c470147fbbc082468d8ff7"}, + {file = "coverage-6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86c91c511853dfda81c2cf2360502cb72783f4b7cebabef27869f00cbe1db07d"}, + {file = "coverage-6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c4ce3b647bd1792d4394f5690d9df6dc035b00bcdbc5595099c01282a59ae01"}, + {file = "coverage-6.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a491e159294d756e7fc8462f98175e2d2225e4dbe062cca7d3e0d5a75ba6260"}, + {file = "coverage-6.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d008e0f67ac800b0ca04d7914b8501312c8c6c00ad8c7ba17754609fae1231a"}, + {file = "coverage-6.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4578728c36de2801c1deb1c6b760d31883e62e33f33c7ba8f982e609dc95167d"}, + {file = "coverage-6.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7ee317486593193e066fc5e98ac0ce712178c21529a85c07b7cb978171f25d53"}, + {file = "coverage-6.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2bc85664b06ba42d14bb74d6ddf19d8bfc520cb660561d2d9ce5786ae72f71b5"}, + {file = "coverage-6.3-cp310-cp310-win32.whl", hash = "sha256:27a94db5dc098c25048b0aca155f5fac674f2cf1b1736c5272ba28ead2fc267e"}, + {file = "coverage-6.3-cp310-cp310-win_amd64.whl", hash = "sha256:bde4aeabc0d1b2e52c4036c54440b1ad05beeca8113f47aceb4998bb7471e2c2"}, + {file = "coverage-6.3-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:509c68c3e2015022aeda03b003dd68fa19987cdcf64e9d4edc98db41cfc45d30"}, + {file = "coverage-6.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e4ff163602c5c77e7bb4ea81ba5d3b793b4419f8acd296aae149370902cf4e92"}, + {file = "coverage-6.3-cp311-cp311-win_amd64.whl", hash = "sha256:d1675db48490e5fa0b300f6329ecb8a9a37c29b9ab64fa9c964d34111788ca2d"}, + {file = "coverage-6.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7eed8459a2b81848cafb3280b39d7d49950d5f98e403677941c752e7e7ee47cb"}, + {file = "coverage-6.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b4285fde5286b946835a1a53bba3ad41ef74285ba9e8013e14b5ea93deaeafc"}, + {file = "coverage-6.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4748349734110fd32d46ff8897b561e6300d8989a494ad5a0a2e4f0ca974fc7"}, + {file = "coverage-6.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:823f9325283dc9565ba0aa2d240471a93ca8999861779b2b6c7aded45b58ee0f"}, + {file = "coverage-6.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:fff16a30fdf57b214778eff86391301c4509e327a65b877862f7c929f10a4253"}, + {file = "coverage-6.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:da1a428bdbe71f9a8c270c7baab29e9552ac9d0e0cba5e7e9a4c9ee6465d258d"}, + {file = "coverage-6.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7d82c610a2e10372e128023c5baf9ce3d270f3029fe7274ff5bc2897c68f1318"}, + {file = "coverage-6.3-cp37-cp37m-win32.whl", hash = "sha256:11e61c5548ecf74ea1f8b059730b049871f0e32b74f88bd0d670c20c819ad749"}, + {file = "coverage-6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:8e0c3525b1a182c8ffc9bca7e56b521e0c2b8b3e82f033c8e16d6d721f1b54d6"}, + {file = "coverage-6.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a189036c50dcd56100746139a459f0d27540fef95b09aba03e786540b8feaa5f"}, + {file = "coverage-6.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:32168001f33025fd756884d56d01adebb34e6c8c0b3395ca8584cdcee9c7c9d2"}, + {file = "coverage-6.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5d79c9af3f410a2b5acad91258b4ae179ee9c83897eb9de69151b179b0227f5"}, + {file = "coverage-6.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:85c5fc9029043cf8b07f73fbb0a7ab6d3b717510c3b5642b77058ea55d7cacde"}, + {file = "coverage-6.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7596aa2f2b8fa5604129cfc9a27ad9beec0a96f18078cb424d029fdd707468d"}, + {file = "coverage-6.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ce443a3e6df90d692c38762f108fc4c88314bf477689f04de76b3f252e7a351c"}, + {file = "coverage-6.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:012157499ec4f135fc36cd2177e3d1a1840af9b236cbe80e9a5ccfc83d912a69"}, + {file = "coverage-6.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0a34d313105cdd0d3644c56df2d743fe467270d6ab93b5d4a347eb9fec8924d6"}, + {file = "coverage-6.3-cp38-cp38-win32.whl", hash = "sha256:6e78b1e25e5c5695dea012be473e442f7094d066925604be20b30713dbd47f89"}, + {file = "coverage-6.3-cp38-cp38-win_amd64.whl", hash = "sha256:433b99f7b0613bdcdc0b00cc3d39ed6d756797e3b078d2c43f8a38288520aec6"}, + {file = "coverage-6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9ed3244b415725f08ca3bdf02ed681089fd95e9465099a21c8e2d9c5d6ca2606"}, + {file = "coverage-6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab4fc4b866b279740e0d917402f0e9a08683e002f43fa408e9655818ed392196"}, + {file = "coverage-6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8582e9280f8d0f38114fe95a92ae8d0790b56b099d728cc4f8a2e14b1c4a18c"}, + {file = "coverage-6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c72bb4679283c6737f452eeb9b2a0e570acaef2197ad255fb20162adc80bea76"}, + {file = "coverage-6.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca29c352389ea27a24c79acd117abdd8a865c6eb01576b6f0990cd9a4e9c9f48"}, + {file = "coverage-6.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:152cc2624381df4e4e604e21bd8e95eb8059535f7b768c1fb8b8ae0b26f47ab0"}, + {file = "coverage-6.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:51372e24b1f7143ee2df6b45cff6a721f3abe93b1e506196f3ffa4155c2497f7"}, + {file = "coverage-6.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:72d9d186508325a456475dd05b1756f9a204c7086b07fffb227ef8cee03b1dc2"}, + {file = "coverage-6.3-cp39-cp39-win32.whl", hash = "sha256:649df3641eb351cdfd0d5533c92fc9df507b6b2bf48a7ef8c71ab63cbc7b5c3c"}, + {file = "coverage-6.3-cp39-cp39-win_amd64.whl", hash = "sha256:e67ccd53da5958ea1ec833a160b96357f90859c220a00150de011b787c27b98d"}, + {file = "coverage-6.3-pp36.pp37.pp38-none-any.whl", hash = "sha256:27ac7cb84538e278e07569ceaaa6f807a029dc194b1c819a9820b9bb5dbf63ab"}, + {file = "coverage-6.3.tar.gz", hash = "sha256:987a84ff98a309994ca77ed3cc4b92424f824278e48e4bf7d1bb79a63cfe2099"}, +] +easyprocess = [ + {file = "EasyProcess-1.1-py3-none-any.whl", hash = "sha256:82eed523a0a5eb12a81fa4eacd9f342caeb3f900eb4b798740e6696ad07e63f9"}, + {file = "EasyProcess-1.1.tar.gz", hash = "sha256:885898302a57aab948973e8b5d32a4229392b9fb2d986ab1d4ffd590e5ba90ec"}, +] +idna = [ + {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, + {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, ] importlib-metadata = [ {file = "importlib_metadata-4.10.1-py3-none-any.whl", hash = "sha256:899e2a40a8c4a1aec681feef45733de8a6c58f3f6a0dbed2eb6574b4387a77b6"}, @@ -433,6 +685,10 @@ mccabe = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] +mock = [ + {file = "mock-4.0.3-py3-none-any.whl", hash = "sha256:122fcb64ee37cfad5b3f48d7a7d51875d7031aaf3d8be7c42e2bee25044eee62"}, + {file = "mock-4.0.3.tar.gz", hash = "sha256:7d3fbbde18228f4ff2f1f119a45cdffa458b4c0dee32eb4d2bb2f82554bac7bc"}, +] mslex = [ {file = "mslex-0.3.0-py2.py3-none-any.whl", hash = "sha256:380cb14abf8fabf40e56df5c8b21a6d533dc5cbdcfe42406bbf08dda8f42e42a"}, {file = "mslex-0.3.0.tar.gz", hash = "sha256:4a1ac3f25025cad78ad2fe499dd16d42759f7a3801645399cce5c404415daa97"}, @@ -492,8 +748,44 @@ pylint = [ {file = "pylint-2.12.2.tar.gz", hash = "sha256:9d945a73640e1fec07ee34b42f5669b770c759acd536ec7b16d7e4b87a9c9ff9"}, ] pyparsing = [ - {file = "pyparsing-3.0.6-py3-none-any.whl", hash = "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4"}, - {file = "pyparsing-3.0.6.tar.gz", hash = "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"}, + {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"}, + {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"}, +] +pyqt5 = [ + {file = "PyQt5-5.15.6-cp36-abi3-macosx_10_13_x86_64.whl", hash = "sha256:33ced1c876f6a26e7899615a5a4efef2167c263488837c7beed023a64cebd051"}, + {file = "PyQt5-5.15.6-cp36-abi3-manylinux1_x86_64.whl", hash = "sha256:9d6efad0377aa78bf081a20ac752ce86096ded18f04c592d98f5b92dc879ad0a"}, + {file = "PyQt5-5.15.6-cp36-abi3-win32.whl", hash = "sha256:9d2dcdaf82263ae56023410a7af15d1fd746c8e361733a7d0d1bd1f004ec2793"}, + {file = "PyQt5-5.15.6-cp36-abi3-win_amd64.whl", hash = "sha256:f411ecda52e488e1d3c5cce7563e1b2ca9cf0b7531e3c25b03d9a7e56e07e7fc"}, + {file = "PyQt5-5.15.6.tar.gz", hash = "sha256:80343bcab95ffba619f2ed2467fd828ffeb0a251ad7225be5fc06dcc333af452"}, +] +pyqt5-qt5 = [ + {file = "PyQt5_Qt5-5.15.2-py3-none-macosx_10_13_intel.whl", hash = "sha256:76980cd3d7ae87e3c7a33bfebfaee84448fd650bad6840471d6cae199b56e154"}, + {file = "PyQt5_Qt5-5.15.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:1988f364ec8caf87a6ee5d5a3a5210d57539988bf8e84714c7d60972692e2f4a"}, + {file = "PyQt5_Qt5-5.15.2-py3-none-win32.whl", hash = "sha256:9cc7a768b1921f4b982ebc00a318ccb38578e44e45316c7a4a850e953e1dd327"}, + {file = "PyQt5_Qt5-5.15.2-py3-none-win_amd64.whl", hash = "sha256:750b78e4dba6bdf1607febedc08738e318ea09e9b10aea9ff0d73073f11f6962"}, +] +pyqt5-sip = [ + {file = "PyQt5_sip-12.9.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6d5bca2fc222d58e8093ee8a81a6e3437067bb22bc3f86d06ec8be721e15e90a"}, + {file = "PyQt5_sip-12.9.0-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:d59af63120d1475b2bf94fe8062610720a9be1e8940ea146c7f42bb449d49067"}, + {file = "PyQt5_sip-12.9.0-cp310-cp310-win32.whl", hash = "sha256:0fc9aefacf502696710b36cdc9fa2a61487f55ee883dbcf2c2a6477e261546f7"}, + {file = "PyQt5_sip-12.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:485972daff2fb0311013f471998f8ec8262ea381bded244f9d14edaad5f54271"}, + {file = "PyQt5_sip-12.9.0-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:d85002238b5180bce4b245c13d6face848faa1a7a9e5c6e292025004f2fd619a"}, + {file = "PyQt5_sip-12.9.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:83c3220b1ca36eb8623ba2eb3766637b19eb0ce9f42336ad8253656d32750c0a"}, + {file = "PyQt5_sip-12.9.0-cp36-cp36m-win32.whl", hash = "sha256:d8b2bdff7bbf45bc975c113a03b14fd669dc0c73e1327f02706666a7dd51a197"}, + {file = "PyQt5_sip-12.9.0-cp36-cp36m-win_amd64.whl", hash = "sha256:69a3ad4259172e2b1aa9060de211efac39ddd734a517b1924d9c6c0cc4f55f96"}, + {file = "PyQt5_sip-12.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:42274a501ab4806d2c31659170db14c282b8313d2255458064666d9e70d96206"}, + {file = "PyQt5_sip-12.9.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:6a8701892a01a5a2a4720872361197cc80fdd5f49c8482d488ddf38c9c84f055"}, + {file = "PyQt5_sip-12.9.0-cp37-cp37m-win32.whl", hash = "sha256:ac57d796c78117eb39edd1d1d1aea90354651efac9d3590aac67fa4983f99f1f"}, + {file = "PyQt5_sip-12.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4347bd81d30c8e3181e553b3734f91658cfbdd8f1a19f254777f906870974e6d"}, + {file = "PyQt5_sip-12.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c446971c360a0a1030282a69375a08c78e8a61d568bfd6dab3dcc5cf8817f644"}, + {file = "PyQt5_sip-12.9.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fc43f2d7c438517ee33e929e8ae77132749c15909afab6aeece5fcf4147ffdb5"}, + {file = "PyQt5_sip-12.9.0-cp38-cp38-win32.whl", hash = "sha256:055581c6fed44ba4302b70eeb82e979ff70400037358908f251cd85cbb3dbd93"}, + {file = "PyQt5_sip-12.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:c5216403d4d8d857ec4a61f631d3945e44fa248aa2415e9ee9369ab7c8a4d0c7"}, + {file = "PyQt5_sip-12.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a25b9843c7da6a1608f310879c38e6434331aab1dc2fe6cb65c14f1ecf33780e"}, + {file = "PyQt5_sip-12.9.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:dd05c768c2b55ffe56a9d49ce6cc77cdf3d53dbfad935258a9e347cbfd9a5850"}, + {file = "PyQt5_sip-12.9.0-cp39-cp39-win32.whl", hash = "sha256:4f8e05fe01d54275877c59018d8e82dcdd0bc5696053a8b830eecea3ce806121"}, + {file = "PyQt5_sip-12.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:b09f4cd36a4831229fb77c424d89635fa937d97765ec90685e2f257e56a2685a"}, + {file = "PyQt5_sip-12.9.0.tar.gz", hash = "sha256:d3e4489d7c2b0ece9d203ae66e573939f7f60d4d29e089c9f11daa17cfeaae32"}, ] pytest = [ {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, @@ -503,6 +795,65 @@ pytest-cov = [ {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, ] +pytest-mock = [ + {file = "pytest-mock-3.6.1.tar.gz", hash = "sha256:40217a058c52a63f1042f0784f62009e976ba824c418cced42e88d5f40ab0e62"}, + {file = "pytest_mock-3.6.1-py3-none-any.whl", hash = "sha256:30c2f2cc9759e76eee674b81ea28c9f0b94f8f0445a1b87762cadf774f0df7e3"}, +] +pytest-qt = [ + {file = "pytest-qt-4.0.2.tar.gz", hash = "sha256:dfc5240dec7eb43b76bcb5f9a87eecae6ef83592af49f3af5f1d5d093acaa93e"}, + {file = "pytest_qt-4.0.2-py2.py3-none-any.whl", hash = "sha256:e03847ac02a890ccaac0fde1748855b9dce425aceba62005c6cfced6cf7d5456"}, +] +pytest-xvfb = [ + {file = "pytest-xvfb-2.0.0.tar.gz", hash = "sha256:c4ba642de05499940db7f65ee111621939be513e3e75c3da9156b7235e2ed8cf"}, + {file = "pytest_xvfb-2.0.0-py3-none-any.whl", hash = "sha256:6d21b46f099c06d6b8b200e73341da3adb73d67e9139c55d617930881779360b"}, +] +pyvirtualdisplay = [ + {file = "PyVirtualDisplay-2.2-py3-none-any.whl", hash = "sha256:3dd8f63d32a134c07c2433f6931e9877172a99a33757f0e5cd146612e1c6380d"}, + {file = "PyVirtualDisplay-2.2.tar.gz", hash = "sha256:3ecda6b183b03ba65dcfdf0019809722480d7b7e10eea6e3a40bf1ba3146bab7"}, +] +requests = [ + {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, + {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, +] +requests-mock = [ + {file = "requests-mock-1.9.3.tar.gz", hash = "sha256:8d72abe54546c1fc9696fa1516672f1031d72a55a1d66c85184f972a24ba0eba"}, + {file = "requests_mock-1.9.3-py2.py3-none-any.whl", hash = "sha256:0a2d38a117c08bb78939ec163522976ad59a6b7fdd82b709e23bb98004a44970"}, +] +"ruamel.yaml" = [ + {file = "ruamel.yaml-0.17.20-py3-none-any.whl", hash = "sha256:810eef9c46523a3f77479c66267a4708255ebe806a2d540078408c2227f011af"}, + {file = "ruamel.yaml-0.17.20.tar.gz", hash = "sha256:4b8a33c1efb2b443a93fcaafcfa4d2e445f8e8c29c528d9f5cdafb7cc9e4004c"}, +] +"ruamel.yaml.clib" = [ + {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6e7be2c5bcb297f5b82fee9c665eb2eb7001d1050deaba8471842979293a80b0"}, + {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:221eca6f35076c6ae472a531afa1c223b9c29377e62936f61bc8e6e8bdc5f9e7"}, + {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win32.whl", hash = "sha256:1070ba9dd7f9370d0513d649420c3b362ac2d687fe78c6e888f5b12bf8bc7bee"}, + {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:77df077d32921ad46f34816a9a16e6356d8100374579bc35e15bab5d4e9377de"}, + {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:cfdb9389d888c5b74af297e51ce357b800dd844898af9d4a547ffc143fa56751"}, + {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7b2927e92feb51d830f531de4ccb11b320255ee95e791022555971c466af4527"}, + {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win32.whl", hash = "sha256:ada3f400d9923a190ea8b59c8f60680c4ef8a4b0dfae134d2f2ff68429adfab5"}, + {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win_amd64.whl", hash = "sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c"}, + {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d67f273097c368265a7b81e152e07fb90ed395df6e552b9fa858c6d2c9f42502"}, + {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:72a2b8b2ff0a627496aad76f37a652bcef400fd861721744201ef1b45199ab78"}, + {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win32.whl", hash = "sha256:9efef4aab5353387b07f6b22ace0867032b900d8e91674b5d8ea9150db5cae94"}, + {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win_amd64.whl", hash = "sha256:846fc8336443106fe23f9b6d6b8c14a53d38cef9a375149d61f99d78782ea468"}, + {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0847201b767447fc33b9c235780d3aa90357d20dd6108b92be544427bea197dd"}, + {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:78988ed190206672da0f5d50c61afef8f67daa718d614377dcd5e3ed85ab4a99"}, + {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win32.whl", hash = "sha256:a49e0161897901d1ac9c4a79984b8410f450565bbad64dbfcbf76152743a0cdb"}, + {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win_amd64.whl", hash = "sha256:bf75d28fa071645c529b5474a550a44686821decebdd00e21127ef1fd566eabe"}, + {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a32f8d81ea0c6173ab1b3da956869114cae53ba1e9f72374032e33ba3118c233"}, + {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7f7ecb53ae6848f959db6ae93bdff1740e651809780822270eab111500842a84"}, + {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win32.whl", hash = "sha256:89221ec6d6026f8ae859c09b9718799fea22c0e8da8b766b0b2c9a9ba2db326b"}, + {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win_amd64.whl", hash = "sha256:31ea73e564a7b5fbbe8188ab8b334393e06d997914a4e184975348f204790277"}, + {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed"}, + {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:1866cf2c284a03b9524a5cc00daca56d80057c5ce3cdc86a52020f4c720856f0"}, + {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win32.whl", hash = "sha256:3fb9575a5acd13031c57a62cc7823e5d2ff8bc3835ba4d94b921b4e6ee664104"}, + {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win_amd64.whl", hash = "sha256:825d5fccef6da42f3c8eccd4281af399f21c02b32d98e113dbc631ea6a6ecbc7"}, + {file = "ruamel.yaml.clib-0.2.6.tar.gz", hash = "sha256:4ff604ce439abb20794f05613c374759ce10e3595d1867764dd1ae675b85acbd"}, +] +six = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] taskipy = [ {file = "taskipy-1.9.0-py3-none-any.whl", hash = "sha256:02bd2c51c7356ed3f7f8853210ada1cd2ab273e68359ee865021c3057eec6615"}, {file = "taskipy-1.9.0.tar.gz", hash = "sha256:449c160b557cdb1d9c17097a5ea4aa0cd5223723ddbaaa5d5032dd16274fb8f0"}, @@ -516,30 +867,43 @@ tomli = [ {file = "tomli-2.0.0.tar.gz", hash = "sha256:c292c34f58502a1eb2bbb9f5bbc9a5ebc37bee10ffb8c2d6bbdfa8eb13cc14e1"}, ] typed-ast = [ - {file = "typed_ast-1.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d8314c92414ce7481eee7ad42b353943679cf6f30237b5ecbf7d835519e1212"}, - {file = "typed_ast-1.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b53ae5de5500529c76225d18eeb060efbcec90ad5e030713fe8dab0fb4531631"}, - {file = "typed_ast-1.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:24058827d8f5d633f97223f5148a7d22628099a3d2efe06654ce872f46f07cdb"}, - {file = "typed_ast-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:a6d495c1ef572519a7bac9534dbf6d94c40e5b6a608ef41136133377bba4aa08"}, - {file = "typed_ast-1.5.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:de4ecae89c7d8b56169473e08f6bfd2df7f95015591f43126e4ea7865928677e"}, - {file = "typed_ast-1.5.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:256115a5bc7ea9e665c6314ed6671ee2c08ca380f9d5f130bd4d2c1f5848d695"}, - {file = "typed_ast-1.5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:7c42707ab981b6cf4b73490c16e9d17fcd5227039720ca14abe415d39a173a30"}, - {file = "typed_ast-1.5.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:71dcda943a471d826ea930dd449ac7e76db7be778fcd722deb63642bab32ea3f"}, - {file = "typed_ast-1.5.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4f30a2bcd8e68adbb791ce1567fdb897357506f7ea6716f6bbdd3053ac4d9471"}, - {file = "typed_ast-1.5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:ca9e8300d8ba0b66d140820cf463438c8e7b4cdc6fd710c059bfcfb1531d03fb"}, - {file = "typed_ast-1.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9caaf2b440efb39ecbc45e2fabde809cbe56272719131a6318fd9bf08b58e2cb"}, - {file = "typed_ast-1.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c9bcad65d66d594bffab8575f39420fe0ee96f66e23c4d927ebb4e24354ec1af"}, - {file = "typed_ast-1.5.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:591bc04e507595887160ed7aa8d6785867fb86c5793911be79ccede61ae96f4d"}, - {file = "typed_ast-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:a80d84f535642420dd17e16ae25bb46c7f4c16ee231105e7f3eb43976a89670a"}, - {file = "typed_ast-1.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:38cf5c642fa808300bae1281460d4f9b7617cf864d4e383054a5ef336e344d32"}, - {file = "typed_ast-1.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5b6ab14c56bc9c7e3c30228a0a0b54b915b1579613f6e463ba6f4eb1382e7fd4"}, - {file = "typed_ast-1.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a2b8d7007f6280e36fa42652df47087ac7b0a7d7f09f9468f07792ba646aac2d"}, - {file = "typed_ast-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:b6d17f37f6edd879141e64a5db17b67488cfeffeedad8c5cec0392305e9bc775"}, - {file = "typed_ast-1.5.1.tar.gz", hash = "sha256:484137cab8ecf47e137260daa20bafbba5f4e3ec7fda1c1e69ab299b75fa81c5"}, + {file = "typed_ast-1.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:183b183b7771a508395d2cbffd6db67d6ad52958a5fdc99f450d954003900266"}, + {file = "typed_ast-1.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:676d051b1da67a852c0447621fdd11c4e104827417bf216092ec3e286f7da596"}, + {file = "typed_ast-1.5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc2542e83ac8399752bc16e0b35e038bdb659ba237f4222616b4e83fb9654985"}, + {file = "typed_ast-1.5.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74cac86cc586db8dfda0ce65d8bcd2bf17b58668dfcc3652762f3ef0e6677e76"}, + {file = "typed_ast-1.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:18fe320f354d6f9ad3147859b6e16649a0781425268c4dde596093177660e71a"}, + {file = "typed_ast-1.5.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:31d8c6b2df19a777bc8826770b872a45a1f30cfefcfd729491baa5237faae837"}, + {file = "typed_ast-1.5.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:963a0ccc9a4188524e6e6d39b12c9ca24cc2d45a71cfdd04a26d883c922b4b78"}, + {file = "typed_ast-1.5.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0eb77764ea470f14fcbb89d51bc6bbf5e7623446ac4ed06cbd9ca9495b62e36e"}, + {file = "typed_ast-1.5.2-cp36-cp36m-win_amd64.whl", hash = "sha256:294a6903a4d087db805a7656989f613371915fc45c8cc0ddc5c5a0a8ad9bea4d"}, + {file = "typed_ast-1.5.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:26a432dc219c6b6f38be20a958cbe1abffcc5492821d7e27f08606ef99e0dffd"}, + {file = "typed_ast-1.5.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7407cfcad702f0b6c0e0f3e7ab876cd1d2c13b14ce770e412c0c4b9728a0f88"}, + {file = "typed_ast-1.5.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f30ddd110634c2d7534b2d4e0e22967e88366b0d356b24de87419cc4410c41b7"}, + {file = "typed_ast-1.5.2-cp37-cp37m-win_amd64.whl", hash = "sha256:8c08d6625bb258179b6e512f55ad20f9dfef019bbfbe3095247401e053a3ea30"}, + {file = "typed_ast-1.5.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:90904d889ab8e81a956f2c0935a523cc4e077c7847a836abee832f868d5c26a4"}, + {file = "typed_ast-1.5.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bbebc31bf11762b63bf61aaae232becb41c5bf6b3461b80a4df7e791fabb3aca"}, + {file = "typed_ast-1.5.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c29dd9a3a9d259c9fa19d19738d021632d673f6ed9b35a739f48e5f807f264fb"}, + {file = "typed_ast-1.5.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:58ae097a325e9bb7a684572d20eb3e1809802c5c9ec7108e85da1eb6c1a3331b"}, + {file = "typed_ast-1.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:da0a98d458010bf4fe535f2d1e367a2e2060e105978873c04c04212fb20543f7"}, + {file = "typed_ast-1.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:33b4a19ddc9fc551ebabca9765d54d04600c4a50eda13893dadf67ed81d9a098"}, + {file = "typed_ast-1.5.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1098df9a0592dd4c8c0ccfc2e98931278a6c6c53cb3a3e2cf7e9ee3b06153344"}, + {file = "typed_ast-1.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42c47c3b43fe3a39ddf8de1d40dbbfca60ac8530a36c9b198ea5b9efac75c09e"}, + {file = "typed_ast-1.5.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f290617f74a610849bd8f5514e34ae3d09eafd521dceaa6cf68b3f4414266d4e"}, + {file = "typed_ast-1.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:df05aa5b241e2e8045f5f4367a9f6187b09c4cdf8578bb219861c4e27c443db5"}, + {file = "typed_ast-1.5.2.tar.gz", hash = "sha256:525a2d4088e70a9f75b08b3f87a51acc9cde640e19cc523c7e41aa355564ae27"}, +] +typer = [ + {file = "typer-0.4.0-py3-none-any.whl", hash = "sha256:d81169725140423d072df464cad1ff25ee154ef381aaf5b8225352ea187ca338"}, + {file = "typer-0.4.0.tar.gz", hash = "sha256:63c3aeab0549750ffe40da79a1b524f60e08a2cbc3126c520ebf2eeaf507f5dd"}, ] typing-extensions = [ {file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"}, {file = "typing_extensions-4.0.1.tar.gz", hash = "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e"}, ] +urllib3 = [ + {file = "urllib3-1.26.8-py2.py3-none-any.whl", hash = "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed"}, + {file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"}, +] wrapt = [ {file = "wrapt-1.13.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:e05e60ff3b2b0342153be4d1b597bbcfd8330890056b9619f4ad6b8d5c96a81a"}, {file = "wrapt-1.13.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:85148f4225287b6a0665eef08a178c15097366d46b210574a658c1ff5b377489"}, diff --git a/pyproject.toml b/pyproject.toml index d091f98..445e8e6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,22 +1,37 @@ [tool.poetry] -name = "project-template" +name = "GatorConfig" version = "0.1.0" -description = "Simple Python project template" -authors = [""] +description = "Autogeneration of GatorGradle configuration files." +authors = ["Daniel Ullrich", "Kobe Coleman", "Paige Downey", "Favour Ojo", "Wesley Long"] license = "MIT" +packages = [ + { include = "gatorconfig" } +] [tool.poetry.dependencies] python = ">=3.7,<4.0" +typer = "^0.4.0" +"ruamel.yaml" = "^0.17.20" +PyQt5 = "^5.15.6" +requests = "^2.27.1" [tool.poetry.dev-dependencies] taskipy = "^1.9.0" pytest = "^6.2.5" pytest-cov = "^3.0.0" pylint = "^2.12.2" +mock = "^4.0.3" +pytest-mock = "^3.6.1" +pytest-qt = "^4.0.2" +pytest-xvfb = "^2.0.0" +requests-mock = "^1.9.3" + +[tool.poetry.scripts] +gatorconfig = "gatorconfig.gator_config:cli" [tool.taskipy.tasks] -test = "pytest --cov-report term-missing --cov-fail-under=70 --cov-branch --cov=hello tests/" -lint = "pylint hello/ tests/" +test = "pytest --cov-report term-missing --cov-fail-under=70 --cov-branch --cov=gatorconfig tests/" +lint = "pylint gatorconfig/ tests/ gatorconfig/gui/" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/tests/test_actions_configuration.py b/tests/test_actions_configuration.py new file mode 100644 index 0000000..29b7c2c --- /dev/null +++ b/tests/test_actions_configuration.py @@ -0,0 +1,21 @@ +"""This module tests the githubactions.create_actions_configuration module""" +import gatorconfig.actions_configuration + +def test_create_configuration_file(tmpdir): + """Check that the configuration file is actually created""" + path = tmpdir.mkdir('sub').join('config.yml') + gatorconfig.actions_configuration.create_configuration_file(path) + assert path.read() == """name: Grade +on: +- push +- pull_request +jobs: + grade: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + - name: Run GatorGradle + uses: GatorEducator/gatorgradle-action@v1 +""" + assert len(tmpdir.listdir()) == 1 diff --git a/tests/test_gator_config.py b/tests/test_gator_config.py new file mode 100644 index 0000000..22ce768 --- /dev/null +++ b/tests/test_gator_config.py @@ -0,0 +1,59 @@ +"""Test functions within gator_config""" +from pytest_mock import MockerFixture +from typer.testing import CliRunner +from gatorconfig.gator_config import cli +from gatorconfig.gator_config import output_file + +runner = CliRunner() +# @pytest.mark.skip(reason="no way of currently testing this") +def test_cli_input(mocker, tmpdir): + """Test cli input with every flag""" + test_dir = tmpdir.mkdir("testing") + # pylint: disable=W0612 + with mocker.patch('builtins.input', return_value=""): + result = runner.invoke(cli, [ + "--name", + "Test", + "--break", + "--fastfail", + "--file", + "Object 1", + "--indent", + 6, + "--commit-count", + 6, + "--output-path", + test_dir + ]) + print("Test Output:", result.stdout) + test_file = test_dir / "gatorgrader.yml" + with mocker.patch('builtins.input', return_value=""): + assert test_file.exists() + with open(test_file, encoding='utf-8') as fle: + print(fle.read()) + +def test_cli_no_input(mocker: MockerFixture, tmpdir): + """Test cli input with no flags""" + test_dir = tmpdir.mkdir("testing") + result = runner.invoke(cli, ["--output-path", test_dir]) + assert result.exit_code == 1 + test_file = test_dir / "gatorgrader.yml" + with mocker.patch('builtins.input', return_value=""): + assert test_file.exists() + with open(test_file, encoding='utf-8') as fle: + #print(fle.read()) + assert "name: Project" in fle.read() + + +#@pytest.mark.parametrize("input_text", ["I am text"]) + +def test_output_file(tmpdir): + """Test creation of file, and if the file has the correct contents""" + input_text = "I am text" + test_dir = tmpdir.mkdir("testing") + test_file_path = test_dir / "gatorgrader.yml" + assert test_file_path.exists() is False + output_file(input_text, test_dir) + assert test_file_path.exists() + with open(test_file_path, encoding='utf-8') as test_file: + assert "I am text" in test_file.read() diff --git a/tests/test_gator_yaml.py b/tests/test_gator_yaml.py new file mode 100644 index 0000000..fb3e3dc --- /dev/null +++ b/tests/test_gator_yaml.py @@ -0,0 +1,121 @@ +"""Testing for gator_yaml""" +import pytest +from gatorconfig.gator_yaml import GatorYaml + + +@pytest.mark.parametrize( + "key,expected", + [ + ("test", "test:\n"), + ("key_1", "key_1:\n"), + ("key2", "key2:\n"), + ], +) +def test_output_key(key, expected): + """Test for gator_yaml output_key function""" + yaml = GatorYaml() + yaml.output_key(key) + assert yaml.output == expected + + +@pytest.mark.parametrize( + "key, value, expected", + [ + ("test", "value", "test: value\n"), + ("key_1", "value 1", "key_1: value 1\n"), + ("key2", "value that is longer!", "key2: value that is longer!\n"), + ], +) +def test_output_key_value(key, value, expected): + """Test for gator_yaml output_key_value function""" + yaml = GatorYaml() + yaml.output_key_value(key, value) + assert yaml.output == expected + + +@pytest.mark.parametrize( + "key,value,expected", + [ + ("key", "value", False), + ("(pure)", "good value", True), + ("(pure)", "(pure) Code", True), + ], +) +def test_is_keyword(key, value, expected): + """Test for gator_yaml is_keyword function""" + yaml = GatorYaml() + assert yaml.is_keyword(key, value) == expected + + +@pytest.mark.parametrize( + "item,expected", + [ + ("item1", " -item1\n"), + ("item 2", " -item 2\n"), + ("item_3", " -item_3\n"), + ], +) +def test_output_list_item(item, expected): + """Test for gator_yaml output_list_item function""" + yaml = GatorYaml() + yaml.output_list_item(item) + assert yaml.output == expected + + +@pytest.mark.parametrize( + "dic,expected", + [ + ({"test": "Bing bong", "test2": ["bing", "bong"], "test3": + {"Indent!": "wooooo!", "wanna see me do it again?": { + "Bada-bing": "bada-boom!", "list?": ["Hello", "Steve"]}}, "test4": "Continue?"}, + "test: Bing " + "bong\ntest2:\n -bing\n -bong\ntest3:\n Indent" + "!: wooooo!\n wanna see me do it again?:\n" + " Bada-bing: bada-boom!\n list" + "?:\n -Hello\n" + " -Steve\ntest4: " + "Continue?\n"), + ({"break": True}, "break: True\n"), + ({"listy!": ["item1", "Item2", True]}, "listy!:\n -item1\n -Item2\n -True\n"), + ({"(pure)": "pure output!"}, "(pure) pure output!\n"), + ({"commits":10}, "--commits 10\n") + ], +) +def test_dump(dic, expected): + """Test for gator_yaml dump function""" + yaml = GatorYaml() + # print(yaml.dump(dic)) + assert yaml.dump(dic) == expected + + +@pytest.mark.parametrize( + "list_in,expected", + [ + (["--test 1", "--test 2"], " --test 1\n --test 2\n"), + (["--single 1 --language Java"], " --single 1 --language Java\n"), + ], +) +def test_enum_file_list(list_in, expected): + """Test for gator_yaml enum_file_list function""" + yaml = GatorYaml() + yaml.enum_file_list(list_in) + assert yaml.output == expected + + +@pytest.mark.parametrize( + "files,expected", + [ + ({'gatorconfig': {'main': {'java': {'test.java': ['']}}}}, + "gatorconfig:\n main:\n java:\n " + " test.java:\n \n"), + ({'gatorconfig': {'main': {'java': {'test.java': [''], 'test2.java': ['']}}}}, + "gatorconfig:\n main:\n java:\n " + "test.java:\n \n test2.java:\n " + " \n"), + ], +) +def test_enum_file_dict(files, expected): + """Test for gator_yaml enum_file_dict function""" + yaml = GatorYaml() + yaml.enum_file_dict(files) + assert yaml.output == expected diff --git a/tests/test_gui_main.py b/tests/test_gui_main.py new file mode 100644 index 0000000..2cde689 --- /dev/null +++ b/tests/test_gui_main.py @@ -0,0 +1,18 @@ +"""This module tests that the GUI opens""" + +from gatorconfig.gui.homepage import Homepage +from gatorconfig.gui.check_file import CheckFile + + +def test_homepage(qtbot): + """Check that the gui opens""" + application = Homepage() + qtbot.addWidget(application) + assert isinstance(application, Homepage) + + +def test_checkfile(qtbot): + """Check that CheckFile is actually a CheckFile""" + check = CheckFile() + qtbot.addWidget(check) + assert isinstance(check, CheckFile) diff --git a/tests/test_say_hello.py b/tests/test_say_hello.py deleted file mode 100644 index 59d988c..0000000 --- a/tests/test_say_hello.py +++ /dev/null @@ -1,20 +0,0 @@ -"""This module tests the hello.say_hello module.""" - -import pytest - -from hello import say_hello - - -@pytest.mark.parametrize( - "name,expected", - [ - ("World", "Hello, World!\n"), - ("Maria", "Hello, Maria!\n"), - ("Saejin", "Hello, Saejin!\n"), - ], -) -def test_greet(capsys, name, expected): - """Check that greet() prints the correct message.""" - say_hello.greet(name) - captured = capsys.readouterr() - assert captured.out == expected diff --git a/tests/test_scrape_releases.py b/tests/test_scrape_releases.py new file mode 100644 index 0000000..e7e06d9 --- /dev/null +++ b/tests/test_scrape_releases.py @@ -0,0 +1,30 @@ +"""Module to test scrape releases""" +import pytest +from gatorconfig.scrape_releases import get_github_releases + +@pytest.mark.parametrize( + "url,expected", + [ + ("https://api.github.com/repos/GatorEducator/GatorGrader/releases", ["v1.0.0", "v1.0.1"]), + ("GatorEducator/GatorGrader", ["v1.0.0", "v1.0.1"]), + ("https://github.com/GatorEducator/gatorgrader", ["v1.0.0", "v1.0.1"]), + ], +) +def test_get_github_release(requests_mock, url, expected): + """Tests successful get_github_release requests""" + requests_mock.get("https://api.github.com/repos/GatorEducator/GatorGrader/releases", + json=[{"name": "v1.0.0"}, {"name": "v1.0.1"}]) + assert get_github_releases(url) == expected + +@pytest.mark.parametrize( + "url", + [ + ("/GatorEducator/GatorGrader/") + ], +) +def test_get_github_release_fail(requests_mock, url): + """Tests unsuccessful get_github_release requests""" + requests_mock.get("https://api.github.com/repos/GatorEducator/GatorGrader/releases", + json=[{"name": "v1.0.0"}, {"name": "v1.0.1"}]) + with pytest.raises(Exception): + get_github_releases(url) diff --git a/tests/test_split_file_path.py b/tests/test_split_file_path.py new file mode 100644 index 0000000..1a04f57 --- /dev/null +++ b/tests/test_split_file_path.py @@ -0,0 +1,32 @@ +"""Test split_file_path function.""" + +import pytest +from gatorconfig.split_file_path import split_file_path + + +def test_output_as_dic(): + """Ensure output is a dictionary.""" + input_dic = {"sample/file.py": ['']} + output = split_file_path(input_dic) + assert isinstance(output, dict) + +@pytest.mark.parametrize( + "test_input,expected", + [({'gatorconfig/main/java/samplelab/SampleLabMain.java': [''], + 'gatorconfig/main/java/samplelab/DataClass.java': ['']}, + {'gatorconfig': {'main': {'java': {'samplelab': + {'SampleLabMain.java': [''], 'DataClass.java': ['']}}}}}), + ({'gatorconfig/main/java/samplelab/SampleLabMain.java': [''], + 'writing/reflection.md': ['']}, {'gatorconfig': {'main': {'java': {'samplelab': + {'SampleLabMain.java': ['']}}}}, 'writing': {'reflection.md': ['']}}), + ({'gatorconfig/main/java/samplelab/DataClass.java': [''], + 'gatorconfig/main/java/HelloWorld.java': ['']}, + {'gatorconfig': {'main': {'java': {'samplelab': {'DataClass.java': ['']}, + 'HelloWorld.java': ['']}}}}) + ] +) + +def test_dic_nesting(test_input, expected): + """Ensure directories are nested correctly.""" + output = split_file_path(test_input) + assert output == expected