Skip to content

Commit 9194ea0

Browse files
committed
fix bugs and add join context formatting
1 parent 14be362 commit 9194ea0

File tree

14 files changed

+165
-88
lines changed

14 files changed

+165
-88
lines changed

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ RUN pip install --upgrade pip
1414
RUN pip3 install -r /tmp/requirements.txt
1515

1616
COPY src /work
17+
COPY tox.ini /work
1718
COPY tests /work/tests
1819
WORKDIR /work

src/sqlint/__init__.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,20 @@
1111
__all__ = [
1212
'parse',
1313
'check',
14-
'format'
14+
'format',
1515
]
1616

1717
# setting logger
1818
logger = logging.getLogger(__name__)
19+
handler = logging.StreamHandler()
20+
LOG_LEVEL = logging.INFO
21+
22+
if LOG_LEVEL == logging.INFO:
23+
formatter = logging.Formatter('%(message)s')
24+
else:
25+
formatter = logging.Formatter('[%(levelname)s]: %(message)s')
26+
handler.setFormatter(formatter)
27+
logger.addHandler(handler)
1928
logger.setLevel(logging.INFO)
2029

2130

src/sqlint/checker/checker.py

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -319,34 +319,39 @@ def check(tree: SyntaxTree, config: Config) -> List[Violation]:
319319
result: List[Violation] = []
320320

321321
# 1. Whether join context and table name are same line.
322-
result.extend(JoinChecker._check_table_name(tree))
322+
result.extend(JoinChecker._check_table_existance(tree))
323323

324324
# 2. Whether join contexts are described fully, for example [inner join], [left outer join], [right outer join]
325-
keyword_style = config.keyword_style
326325
expected_kvs = {
327-
'left': ['left', 'outer', 'join'],
328-
'outer': ['left', 'outer', 'join'],
329-
'right': ['right', 'outer', 'join'],
330-
'inner': ['inner', 'join'],
331-
'cross': ['cross', 'join'],
326+
'LEFT': ['LEFT', 'OUTER', 'JOIN'],
327+
'RIGHT': ['RIGHT', 'OUTER', 'JOIN'],
328+
'FULL': ['FULL', 'OUTER', 'JOIN'],
329+
'OUTER': ['[LEFT|RIGHT|FULL]', 'OUTER', 'JOIN'],
330+
'INNER': ['INNER', 'JOIN'],
331+
'CROSS': ['CROSS', 'JOIN'],
332332
}
333333
expected_list = {}
334334
for k, vs in expected_kvs.items():
335-
expected_list[k] = ' '.join([
336-
format_keyword(v, keyword_style) for v in vs])
335+
_key = JoinChecker._format_str(k)
336+
_value = ' '.join([JoinChecker._format_str(v) for v in vs])
337+
expected_list[_key] = _value
337338

338339
result.extend(JoinChecker._check_context(tree, expected_list))
339340

340341
return result
341342

342343
@staticmethod
343-
def _check_table_name(tree: SyntaxTree) -> List[Violation]:
344+
def _format_str(value: str) -> str:
345+
return value.upper()
346+
347+
@staticmethod
348+
def _check_table_existance(tree: SyntaxTree) -> List[Violation]:
344349
"""Checks the token next to 'Join' is identifier(maybe table_name) or SubQuery """
345350
violation_list: List[Violation] = list()
346351

347352
for leaf in tree.leaves:
348353
for idx, token in enumerate(leaf.tokens):
349-
# ignores except join
354+
# ignores token except join
350355
if token.word.upper() != 'JOIN':
351356
continue
352357

@@ -370,10 +375,10 @@ def _check_table_name(tree: SyntaxTree) -> List[Violation]:
370375
Join (Select id From y)
371376
------
372377
"""
373-
v = violation.JoinTableViolation(tree=leaf, index=idx)
378+
v = violation.JoinTableNotExistViolation(tree=leaf, index=idx)
374379
violation_list.append(v)
375380

376-
violation_list.extend(JoinChecker._check_table_name(leaf))
381+
violation_list.extend(JoinChecker._check_table_existance(leaf))
377382

378383
return violation_list
379384

@@ -388,28 +393,26 @@ def _check_context(tree: SyntaxTree, expected_list: Dict[str, str]) -> List[Viol
388393

389394
for idx in join_indexes:
390395
token = leaf.tokens[idx]
391-
# ignores except join
392-
if token.kind != Token.KEYWORD or token.word.upper() != 'JOIN':
393-
continue
394396

395397
# concat keyword concerned with join
396398
join_contexts = [token.word]
397-
expected: str = expected_list['inner']
399+
# only 'JOIN' is expected INNER JOIN
400+
expected: str = expected_list[JoinChecker._format_str('INNER')]
398401
for tk in reversed(leaf.tokens[:idx]):
399-
if tk.kind == Token.WHITESPACE:
402+
if tk.kind in [Token.WHITESPACE, Token.COMMENT]:
400403
continue
401404

402-
if tk.word.upper() in ['INNER', 'OUTER', 'LEFT', 'RIGHT', 'CROSS']:
405+
word = JoinChecker._format_str(tk.word)
406+
if word in expected_list.keys():
403407
join_contexts.insert(0, tk.word)
404-
if tk.word.lower() in expected_list:
405-
expected = expected_list[tk.word.lower()]
408+
expected = expected_list[word]
406409
else:
407410
break
408411

409-
join_context_str = ' '.join(join_contexts)
410-
if join_context_str.upper() not in ['INNER JOIN', 'RIGHT OUTER JOIN', 'LEFT OUTER JOIN', 'CROSS JOIN']:
412+
join_context_str = JoinChecker._format_str(' '.join(join_contexts))
413+
if join_context_str not in expected_list.values():
411414
params = {'actual': join_context_str, 'expected': expected}
412-
v = violation.JoinContextViolation(tree=leaf, index=idx, **params)
415+
v = violation.JoinContextOmitViolation(tree=leaf, index=idx, **params)
413416
violation_list.append(v)
414417

415418
violation_list.extend(JoinChecker._check_context(leaf, expected_list))

src/sqlint/checker/violation.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ class Code(Enum):
2424
KEYWORD_UPPER_HEAD = ('E402', 'a head of reserved keywords must be upper case: {actual} -> {expected}')
2525
KEYWORD_LOWER = ('E403', 'reserved keywords must be lower case: {actual} -> {expected}')
2626
# E5: Join Context
27-
JOIN_TABLE = ('E501', 'table_name must be at the same line as join context')
28-
JOIN_CONTEXT = ('E502', 'join context must be fully: {actual} -> {expected}')
27+
JOIN_TABLE_NOT_EXISIT = ('E501', 'table_name must be at the same line as join context')
28+
JOIN_CONTEXT_OMIT = ('E502', 'join context must be fully: {actual} -> {expected}')
2929
# E6: lines
3030
LINE_BlANK_MULTIPLE = ('E601', 'there are multiple blank lines')
3131
LINE_ONLY_WHITESPACE = ('E602', 'this line has only whitespace')
@@ -153,14 +153,14 @@ def __init__(self, tree: SyntaxTree, index: int, **kwargs):
153153
super().__init__(tree, index, _code, **kwargs)
154154

155155

156-
class JoinTableViolation(Violation):
156+
class JoinTableNotExistViolation(Violation):
157157
def __init__(self, tree: SyntaxTree, index: int, **kwargs):
158-
super().__init__(tree, index, Code.JOIN_TABLE, **kwargs)
158+
super().__init__(tree, index, Code.JOIN_TABLE_NOT_EXISIT, **kwargs)
159159

160160

161-
class JoinContextViolation(Violation):
161+
class JoinContextOmitViolation(Violation):
162162
def __init__(self, tree: SyntaxTree, index: int, **kwargs):
163-
super().__init__(tree, index, Code.JOIN_CONTEXT, **kwargs)
163+
super().__init__(tree, index, Code.JOIN_CONTEXT_OMIT, **kwargs)
164164

165165

166166
class MultiBlankLineViolation(Violation):

src/sqlint/cli.py

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,6 @@
1010

1111
# setting logger
1212
logger = logging.getLogger(__name__)
13-
formatter = logging.Formatter('%(message)s')
14-
handler = logging.StreamHandler()
15-
handler.setFormatter(formatter)
16-
logger.addHandler(handler)
17-
logger.setLevel(logging.INFO)
1813

1914

2015
@click.command(context_settings={'ignore_unknown_options': True})
@@ -44,10 +39,12 @@ def main(files, config_file, is_format):
4439
# constructs syntax tree in each files
4540
for f in files:
4641
if not os.path.exists(f):
47-
logger.warning(FileNotFoundError(f))
42+
logger.warning(f'file is not found: {f}')
43+
continue
4844

4945
if os.path.isdir(f):
50-
logger.warning(IsADirectoryError(f))
46+
logger.warning(f'{f} is a directory')
47+
continue
5148

5249
with open(f, 'r') as fp:
5350
if is_format:
@@ -56,8 +53,6 @@ def main(files, config_file, is_format):
5653
else:
5754
trees[f] = SyntaxTree.sqlptree(fp.read())
5855

59-
logger.debug(trees[f])
60-
6156
for file, tree in trees.items():
6257
if is_format:
6358
formatted_tree = format_tree(tree, config)

src/sqlint/config/config_loader.py

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import logging
23
import warnings
34
from typing import Optional
45
from configparser import (
@@ -22,24 +23,34 @@
2223
}
2324

2425

26+
logger = logging.getLogger(__name__)
27+
28+
2529
class ConfigLoader:
2630
def __init__(self, config_file: Optional[str] = DEFAULT_INI):
2731
self.values = {}
2832

33+
if not os.path.exists(DEFAULT_INI):
34+
raise FileNotFoundError(f'default setting file is not found: {DEFAULT_INI}')
35+
2936
# load default configs
3037
default_config = ConfigParser()
3138
default_config.read(DEFAULT_INI)
3239
self._load(default_config)
3340

3441
# load user config
3542
self.user_config_file: Optional[str]
36-
if config_file is not None and config_file != DEFAULT_INI:
43+
if config_file != DEFAULT_INI:
3744
self.user_config_file = config_file
38-
user_config = ConfigParser()
39-
user_config.read(config_file)
4045

41-
# load user configs
42-
self._load(user_config)
46+
if not os.path.exists(self.user_config_file):
47+
logger.warning(f'[Warning] config file is not found: {config_file}')
48+
else:
49+
user_config = ConfigParser()
50+
user_config.read(self.user_config_file)
51+
52+
# load user configs
53+
self._load(user_config)
4354

4455
@staticmethod
4556
def _get_with_type(config_parser: ConfigParser, name: str, _type: type):
@@ -64,12 +75,8 @@ def _get_with_type(config_parser: ConfigParser, name: str, _type: type):
6475
return config_parser.get(SECTION, name)
6576

6677
def _load(self, config_parser: ConfigParser):
67-
"""Loads config values
78+
"""Loads config values """
6879

69-
Returns:
70-
71-
"""
72-
# load default settings
7380
for name, _type in NAME_TYPES.items():
7481
try:
7582
self.values[name] = self._get_with_type(config_parser, name, _type)
@@ -83,15 +90,7 @@ def _load(self, config_parser: ConfigParser):
8390
raise e
8491

8592
def get(self, name, default=None):
86-
"""
87-
88-
Args:
89-
name:
90-
default:
91-
92-
Returns:
93-
94-
"""
93+
"""Returns value by name"""
9594
if name in self.values:
9695
return self.values[name]
9796

src/sqlint/formatter/base.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from sqlint.parser import Token
88
from sqlint.syntax_tree import SyntaxTree
99

10-
1110
logger = logging.getLogger(__name__)
1211

1312

@@ -68,12 +67,11 @@ def _reshape_tree(tree: SyntaxTree, config: Config):
6867
own, children, sibling = _split_tokens(tree)
6968
siblings = [sibling]
7069

71-
"""DEBUG
72-
if own and own[0].word.lower() == 'date_diff':
73-
logger.debug(f'\033[32mown\033[0m = {own}')
74-
logger.debug(f'\033[32mchildren\033[0m = {children}')
75-
logger.debug(f'\033[32msibling\033[0m = {sibling}')
76-
"""
70+
# f own and own[0].word.lower() == 'date_diff':
71+
logger.debug(f'\033[32mown\033[0m = {own}')
72+
logger.debug(f'\033[32mchildren\033[0m = {children}')
73+
logger.debug(f'\033[32msibling\033[0m = {sibling}')
74+
7775
tree.tokens = own
7876

7977
# checks tokens(line) length
@@ -152,6 +150,7 @@ def _format_tree(tree: SyntaxTree, config: Config):
152150
# formetter order is important
153151
formatter_list = [
154152
fmt.KeywordStyleFormatter,
153+
fmt.JoinFormatter,
155154
fmt.CommaPositionFormatter,
156155
fmt.IndentStepsFormatter,
157156
fmt.BlankLineFormatter,

0 commit comments

Comments
 (0)