Skip to content

Commit

Permalink
Replaced "print" with "logging" in PythonTA check and check_contracts (
Browse files Browse the repository at this point in the history
  • Loading branch information
merrickliu888 authored Dec 11, 2023
1 parent 53a4cc7 commit 2ce5460
Show file tree
Hide file tree
Showing 11 changed files with 230 additions and 36 deletions.
60 changes: 32 additions & 28 deletions python_ta/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
pass

import importlib.util
import logging
import os
import sys
import tokenize
Expand All @@ -55,10 +56,6 @@

HELP_URL = "http://www.cs.toronto.edu/~david/pyta/checkers/index.html"

# check the python version
if sys.version_info < (3, 7, 0):
print("[WARNING] You need Python 3.7 or later to run PythonTA.")


# Flag to determine if we've previously patched pylint
PYLINT_PATCHED = False
Expand Down Expand Up @@ -115,6 +112,13 @@ def _check(
`load_default_config` is used to specify whether to load the default .pylintrc file that comes
with PythonTA. It will load it by default.
"""
# Configuring logger
logging.basicConfig(format="[%(levelname)s] %(message)s", level=logging.NOTSET)

# check the python version
if sys.version_info < (3, 7, 0):
logging.warning("You need Python 3.7 or later to run PythonTA.")

linter = reset_linter(config=local_config, load_default_config=load_default_config)
current_reporter = linter.reporter
current_reporter.set_output(output)
Expand Down Expand Up @@ -174,17 +178,15 @@ def _check(
current_reporter.print_messages(level)
if linter.config.pyta_file_permission:
f_paths.append(file_py) # Appending paths for upload
print(
"[INFO] File: {} was checked using the configuration file: {}".format(
logging.info(
"File: {} was checked using the configuration file: {}".format(
file_py, linter.config_file
),
file=sys.stderr,
)
)
print(
"[INFO] File: {} was checked using the messages-config file: {}".format(
logging.info(
"File: {} was checked using the messages-config file: {}".format(
file_py, messages_config_path
),
file=sys.stderr,
)
)
if linter.config.pyta_error_permission:
errs = list(current_reporter.messages.values())
Expand All @@ -206,10 +208,10 @@ def _check(
linter.generate_reports()
return current_reporter
except Exception as e:
print(
"[ERROR] Unexpected error encountered! Please report this to your instructor (and attach the code that caused the error)."
logging.error(
"Unexpected error encountered! Please report this to your instructor (and attach the code that caused the error)."
)
print('[ERROR] Error message: "{}"'.format(e))
logging.error('Error message: "{}"'.format(e))
raise e


Expand Down Expand Up @@ -386,32 +388,32 @@ def _verify_pre_check(filepath: AnyStr, allow_pylint_comments: bool) -> bool:
continue
match = OPTION_PO.search(content)
if match is not None:
print(
'[ERROR] String "pylint:" found in comment. '
logging.error(
'String "pylint:" found in comment. '
+ "No check run on file `{}.`\n".format(filepath)
)
return False
except IndentationError as e:
print(
"[ERROR] python_ta could not check your code due to an "
logging.error(
"python_ta could not check your code due to an "
+ "indentation error at line {}.".format(e.lineno)
)
return False
except tokenize.TokenError as e:
print(
"[ERROR] python_ta could not check your code due to a " + "syntax error in your file."
logging.error(
"python_ta could not check your code due to a " + "syntax error in your file."
)
return False
except UnicodeDecodeError:
print(
"[ERROR] python_ta could not check your code due to an "
logging.error(
"python_ta could not check your code due to an "
+ "invalid character. Please check the following lines "
"in your file and all characters that are marked with a �."
)
with open(os.path.expanduser(filepath), encoding="utf-8", errors="replace") as f:
for i, line in enumerate(f):
if "�" in line:
print(f" Line {i}: {line}", end="")
logging.error(f" Line {i + 1}: {line}")
return False
return True

Expand All @@ -428,7 +430,7 @@ def _get_valid_files_to_check(module_name: Union[List[str], str]) -> Generator[A
module_name = [module_name]
# Otherwise, enforce API to expect `module_name` type as list
elif not isinstance(module_name, list):
print(
logging.error(
"No checks run. Input to check, `{}`, has invalid type, must be a list of strings.".format(
module_name
)
Expand All @@ -438,7 +440,9 @@ def _get_valid_files_to_check(module_name: Union[List[str], str]) -> Generator[A
# Filter valid files to check
for item in module_name:
if not isinstance(item, str): # Issue errors for invalid types
print("No check run on file `{}`, with invalid type. Must be type: str.\n".format(item))
logging.error(
"No check run on file `{}`, with invalid type. Must be type: str.\n".format(item)
)
elif os.path.isdir(item):
yield item
elif not os.path.exists(os.path.expanduser(item)):
Expand All @@ -448,9 +452,9 @@ def _get_valid_files_to_check(module_name: Union[List[str], str]) -> Generator[A
if os.path.exists(filepath):
yield filepath
else:
print("Could not find the file called, `{}`\n".format(item))
logging.error("Could not find the file called, `{}`\n".format(item))
except ImportError:
print("Could not find the file called, `{}`\n".format(item))
logging.error("Could not find the file called, `{}`\n".format(item))
else:
yield item # Check other valid files.

Expand Down
6 changes: 3 additions & 3 deletions python_ta/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Within the config submodule, this .py file encompasses functions responsible
for managing all configuration-related tasks.
"""

import logging
import os
import sys
from pathlib import Path
Expand Down Expand Up @@ -48,7 +48,7 @@ def override_config(linter: PyLinter, config_location: AnyStr) -> None:
try:
_, config_args = config_file_parser.parse_config_file(file_path=config_location)
except OSError as ex:
print(ex, file=sys.stderr)
logging.error(ex)
sys.exit(32)

# Override the config options by parsing the provided file.
Expand Down Expand Up @@ -76,7 +76,7 @@ def load_messages_config(path: str, default_path: str, use_pyta_error_messages:
try:
merge_from = toml.load(path)
except FileNotFoundError:
print(f"[WARNING] Could not find messages config file at {str(Path(path).resolve())}.")
logging.warning(f"Could not find messages config file at {str(Path(path).resolve())}.")
merge_from = {}

if not use_pyta_error_messages:
Expand Down
5 changes: 3 additions & 2 deletions python_ta/contracts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
[(assertion, compiled, return_val_var_name)].
"""
import inspect
import logging
import sys
import typing
from types import CodeType, FunctionType, ModuleType
Expand Down Expand Up @@ -613,8 +614,8 @@ def _debug(msg: str) -> None:
"""
if not DEBUG_CONTRACTS:
return

print("[PyTA]", msg, file=sys.stderr)
logging.basicConfig(format="[%(levelname)s] %(message)s", level=logging.DEBUG)
logging.debug(msg)


def _set_invariants(klass: type) -> None:
Expand Down
1 change: 1 addition & 0 deletions tests/fixtures/pylint_comment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# pylint: disable=unbalanced-tuple-unpacking
5 changes: 5 additions & 0 deletions tests/fixtures/unicode_decode_error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"�"
"�"
"foo"
"foo"
"�"
5 changes: 3 additions & 2 deletions tests/test_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""
import subprocess
import sys
from os import path, remove
from os import environ, path, remove
from unittest.mock import Mock

import python_ta
Expand Down Expand Up @@ -254,7 +254,8 @@ def test_check_exit_zero() -> None:
"--config",
"tests/test.pylintrc",
"examples/nodes/name.py",
]
],
env={**environ, "PYTHONIOENCODING": "utf-8"},
)

assert output.returncode == 0
Expand Down
22 changes: 22 additions & 0 deletions tests/test_config/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
from unittest.mock import mock_open, patch

import pytest
from pylint import lint

import python_ta
from python_ta.config import load_messages_config, override_config

TEST_CONFIG = {
"pyta-number-of-messages": 10,
Expand Down Expand Up @@ -229,6 +231,26 @@ def test_config_parse_error_has_no_snippet() -> None:
assert snippet == ""


def test_override_config_logging(caplog) -> None:
"""Testing that the OSError in override_config is logged correctly"""
path = "C:\\foo\\tests\\file_fixtures\\test_f0011.pylintrc"
linter = lint.PyLinter()

with pytest.raises(SystemExit):
override_config(linter, path)
assert caplog.records[0].levelname == "ERROR"
assert f"The config file {path} doesn't exist!" in caplog.text


@patch("python_ta.config.toml.load", side_effect=FileNotFoundError)
def test_load_messages_config_logging(_, caplog):
try:
load_messages_config("non_existent_file.toml", "default_file.toml", True)
except FileNotFoundError:
assert "Could not find messages config file at" in caplog.text
assert "WARNING" in [record.levelname for record in caplog.records]


def test_allow_pylint_comments() -> None:
"""Test that checks whether the allow-pylint-comments configuration option works as expected when it is
set to True
Expand Down
1 change: 0 additions & 1 deletion tests/test_config/test_num_error_occurrences.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""
Test suite for checking that the correct number of error occurrences are being displayed.
"""

import contextlib
import io
import os
Expand Down
29 changes: 29 additions & 0 deletions tests/test_contracts/test_contracts_debug.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import logging

from python_ta import contracts

contracts.DEBUG_CONTRACTS = True
from python_ta.contracts import check_contracts


def test_contracts_debug(caplog) -> None:
"""Test to see if _debug function is logging messages correctly"""
caplog.set_level(logging.DEBUG)

@check_contracts
def divide(x: int, y: int) -> int:
"""Return x // y.
Preconditions:
- invalid precondition
"""
return x // y

divide(6, 2)

for record in caplog.records:
assert record.levelname == "DEBUG"
assert (
"Warning: precondition invalid precondition could not be parsed as a valid Python expression"
in caplog.text
)
Loading

0 comments on commit 2ce5460

Please sign in to comment.