Skip to content

Commit

Permalink
Merge branch 'main' of github.com:spectriclabs/xmextras into main
Browse files Browse the repository at this point in the history
  • Loading branch information
Sean Sullivan committed Jun 17, 2024
2 parents 60d84a9 + 8d3619b commit 6de2d6d
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 30 deletions.
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Extra helper functionality for X-Midas.
Wraps an `XMSession` in with a context manager to ensure it gets ended correctly when exiting.

```python
import xmextas as xmx
import xmextras as xmx

with xmx.XMSessionContext() as session:
session.xm('res answer 42')
Expand All @@ -24,7 +24,7 @@ with xmx.XMSessionContext() as session:
Returns a dictionary of information about the X-Midas instance, which can be useful for tagging metrics and benchmarks for comparison.

```python
import xmextas as xmx
import xmextras as xmx

with xmx.XMSessionContext() as session:
info = xmx.info(session) # also works with a normal XMSession instance
Expand Down Expand Up @@ -57,4 +57,8 @@ xmextras locates the system installation of bluefile.py so it can be imported ou
```python
from xmextras import bluefile
header = bluefile.readheader('some_bluefile.tmp')
```
```

## mcrlint

The `mcrlint` command is also included as part of xmextras. It can be used for linting X-Midas macro code. Use `mcrlint --help` for more information.
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ readme = "README.md"
python = "^3.9"
numpy = "^1.20.1"

[tool.poetry.scripts]
mcrlint = 'xmextras.mcrlint:mcrlint'

[build-system]
requires = ["poetry-core"]
Expand Down
19 changes: 19 additions & 0 deletions tests/test_mcrlint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import pytest

import xmextras.mcrlint

def test_check_line(tmp_path):
bad_macro_path = tmp_path / 'bad_macro.txt'

with bad_macro_path.open('w') as out:
out.write(' ' * 201)
out.write('\n')
out.write(' do_thing ! TODO: make do thing\n')
out.write('\tdo_other_thing\n')
out.write(' sloppy ! FIXME \n')

issues = list(xmextras.mcrlint.check_file(bad_macro_path, xmextras.mcrlint.get_active_plugins('')))
assert len(issues) == 6

issues = list(xmextras.mcrlint.check_file(bad_macro_path, xmextras.mcrlint.get_active_plugins('fixme')))
assert len(issues) == 4
92 changes: 65 additions & 27 deletions xmextras/mcrlint.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,59 @@
from argparse import ArgumentParser
from pathlib import Path

import re
import sys


class Plugin:
pass
def __str__(self):
return f'{self.id} = {self.message}' # pylint: disable=no-member


class TrailingWhitespacePlugin(Plugin):
class FixmePlugin(Plugin):
def __init__(self):
self.id = 'TrailingWhitespace'
self.message = 'Trailing whitespace detected'
self.pat = re.compile(r'[ \t]+$')
self.id = 'fixme'
self.message = 'TODO, FIXME, or XXX comment detected'
self.pat = re.compile(r'![ \t\-]+(TODO|FIXME|XXX)')

def check(self, line):
return self.pat.search(line)


class FixmePlugin(Plugin):
def __init__(self):
self.id = 'Fixme'
self.message = 'TODO, FIXME, or XXX comment detected'
self.pat = re.compile(r'![ \t\-]+ (TODO|FIXME|XXX)')
class LineTooLongPlugin(Plugin):
def __init__(self, limit=200):
self.id = 'line-too-long'
self.limit = limit
self.message = f'Line greater than {limit} characters long'

def check(self, line):
return self.pat.search(line)
return len(line) > self.limit


class TabCharPlugin(Plugin):
def __init__(self):
self.id = 'TabChar'
self.id = 'tab-char'
self.message = 'Tab character detected'

def check(self, line):
return '\t' in line


class TrailingWhitespacePlugin(Plugin):
def __init__(self):
self.id = 'trailing-whitespace'
self.message = 'Trailing whitespace detected'
self.pat = re.compile(r'[ \t]+$')

def check(self, line):
return self.pat.search(line)


PLUGINS = [
TrailingWhitespacePlugin(),
FixmePlugin(),
LineTooLongPlugin(),
TabCharPlugin(),
TrailingWhitespacePlugin(),
]


Expand All @@ -64,15 +77,7 @@ def __str__(self):
return f'{self.path}: {self.line_number}: {self.plugin.message} ({self.plugin.id})'


def check_file(path: Path, disable=None):
if disable is None:
disable = []

if not isinstance(disable, list):
raise ValueError("Expected list of disabled plugin ID's or None")

plugins = [plugin for plugin in PLUGINS if plugin.id not in disable]

def check_file(path: Path, plugins):
with path.open('r', encoding='utf8') as f:
for line_num, line in enumerate(f):
line_issues = check_line(line, plugins)
Expand All @@ -81,16 +86,49 @@ def check_file(path: Path, disable=None):
yield Issue(path, line_num+1, issue)


def mcrlint():
paths = [Path(p) for p in sys.argv[1:]]
issue_count = 0
def all_paths_exist(paths):
num_missing = 0

for path in paths:
if not path.exists():
print(f'Could not find {path}')
continue
num_missing += 1

return num_missing == 0


def get_active_plugins(disable):
disabled_plugins = [p.lower() for p in disable.split(',')]
return [p for p in PLUGINS if p.id not in disabled_plugins]


def mcrlint():
parser = ArgumentParser(description='Linter for X-Midas macro code')
parser.add_argument('-d', '--disable', default='', help='Comma-separated list of check plugins to disable')
parser.add_argument('--list', action='store_true', help='List check plugins')
parser.add_argument('filenames', nargs='*')
args = parser.parse_args()

for issue in check_file(path):
if args.list:
for plugin in PLUGINS:
print(str(plugin))
sys.exit(0)

paths = [Path(p) for p in args.filenames]

if len(paths) == 0:
print('No paths specified')
sys.exit(1)

plugins = get_active_plugins(args.disable)

if not all_paths_exist(paths):
sys.exit(1)

issue_count = 0

for path in paths:
for issue in check_file(path, plugins):
print(issue)
issue_count += 1

Expand Down

0 comments on commit 6de2d6d

Please sign in to comment.