Skip to content

Commit 7b45df1

Browse files
committed
Setup and initial version
1 parent 7aae6ff commit 7b45df1

File tree

5 files changed

+260
-0
lines changed

5 files changed

+260
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,6 @@ dmypy.json
123123

124124
# Pyre type checker
125125
.pyre/
126+
127+
# VS Code Config
128+
.vscode/

requirements.txt

Whitespace-only changes.

setup.cfg

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[flake8]
2+
ignore = E501

setup.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# /usr/bin/env python
2+
import os
3+
from setuptools import setup
4+
from setuptools.command.test import test as TestCommand, Command
5+
6+
7+
class PyTest(TestCommand):
8+
def finalize_options(self):
9+
TestCommand.finalize_options(self)
10+
self.test_args = ["tests", "-s"]
11+
self.test_suite = True
12+
13+
def run_tests(self):
14+
import pytest
15+
16+
errno = pytest.main(self.test_args)
17+
raise SystemExit(errno)
18+
19+
20+
class LintCommand(Command):
21+
"""
22+
A copy of flake8's Flake8Command
23+
"""
24+
25+
description = "Run flake8 on modules registered in setuptools"
26+
user_options = []
27+
28+
def initialize_options(self):
29+
# must override
30+
pass
31+
32+
def finalize_options(self):
33+
# must override
34+
pass
35+
36+
def distribution_files(self):
37+
if self.distribution.packages:
38+
for package in self.distribution.packages:
39+
yield package.replace(".", os.path.sep)
40+
41+
if self.distribution.py_modules:
42+
for filename in self.distribution.py_modules:
43+
yield "%s.py" % filename
44+
45+
def run(self):
46+
from flake8.api.legacy import get_style_guide
47+
48+
flake8_style = get_style_guide(config_file="setup.cfg")
49+
paths = self.distribution_files()
50+
report = flake8_style.check_files(paths)
51+
raise SystemExit(report.total_errors > 0)
52+
53+
54+
with open("README.md", "r") as fh:
55+
long_description = fh.read()
56+
57+
with open("requirements.txt") as fh:
58+
requirements = fh.read().split("\n")
59+
60+
setup(
61+
name="table2ascii",
62+
version="1.0.0",
63+
author="Jonah Lawrence",
64+
author_email="jonah@freshidea.com",
65+
description="Convert 2D Python lists into Unicode/Ascii tables",
66+
long_description=long_description,
67+
long_description_content_type="text/markdown",
68+
url="https://github.com/DenverCoder1/table2ascii",
69+
packages=["table2ascii"],
70+
classifiers=[
71+
"Environment :: Web Environment",
72+
"Intended Audience :: Developers",
73+
"License :: OSI Approved :: MIT License",
74+
"Operating System :: OS Independent",
75+
"Programming Language :: Python :: 2.5",
76+
"Programming Language :: Python :: 2.6",
77+
"Programming Language :: Python :: 2.7",
78+
"Programming Language :: Python :: 3.6",
79+
"Programming Language :: Python :: 3.7",
80+
"Programming Language :: Python :: 3.8",
81+
"Topic :: Utilities",
82+
],
83+
python_requires=">=3.6",
84+
install_requires=[requirements],
85+
setup_requires=[
86+
"flake8>=3.8,<4",
87+
],
88+
tests_require=[
89+
"pytest>=6.2,<7",
90+
],
91+
cmdclass={
92+
"test": PyTest,
93+
"lint": LintCommand,
94+
},
95+
)

table2ascii/__init__.py

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
from typing import List
2+
3+
4+
class TableToAscii:
5+
def __init__(
6+
self,
7+
header_row: List[str],
8+
body: List[List[str]],
9+
footer_row: List[str],
10+
):
11+
self.header_row = header_row
12+
self.body = body
13+
self.footer_row = footer_row
14+
self.cell_width = 5
15+
"""
16+
╔═════╦═══════════════════════╗
17+
║ # ║ G H R S ║
18+
╟─────╫───────────────────────╢
19+
║ 1 ║ 30 40 35 30 ║
20+
║ 2 ║ 30 40 35 30 ║
21+
╟─────╫───────────────────────╢
22+
║ SUM ║ 130 140 135 130 ║
23+
╚═════╩═══════════════════════╝
24+
"""
25+
self.parts = {
26+
"top_left_corner": "╔",
27+
"top_right_corner": "╗",
28+
"top_edge": "═",
29+
"first_col_top_tee": "╦",
30+
"top_tee": "═",
31+
"left_edge": "║",
32+
"header_row_sep": "─",
33+
"footer_row_sep": "─",
34+
"first_col_sep": "║",
35+
"left_tee": "╟",
36+
"middle_edge": " ",
37+
"header_row_cross": "─",
38+
"footer_row_cross": "─",
39+
"first_col_cross": "╫",
40+
"right_edge": "║",
41+
"right_tee": "╢",
42+
"bottom_left_corner": "╚",
43+
"bottom_right_corner": "╝",
44+
"bottom_edge": "═",
45+
"first_col_bottom_tee": "╩",
46+
"bottom_tee": "═",
47+
}
48+
49+
def to_ascii(self) -> str:
50+
cols = len(self.header_row)
51+
# create table header
52+
table = [
53+
# ╔
54+
self.parts["top_left_corner"]
55+
# ═════╦
56+
+ self.parts["top_edge"] * self.cell_width + self.parts["first_col_top_tee"]
57+
#
58+
+ (
59+
(self.parts["top_edge"] * self.cell_width + self.parts["top_tee"])
60+
* (cols - 1)
61+
)[0:-1]
62+
# ╗
63+
+ self.parts["top_right_corner"],
64+
# ║
65+
self.parts["left_edge"]
66+
# # ║
67+
+ f" {self.header_row[0]} " + self.parts["first_col_sep"]
68+
# G H R S
69+
+ self.parts["middle_edge"].join(
70+
" " + val + " " for val in self.header_row[1:]
71+
)
72+
# ║
73+
+ self.parts["right_edge"],
74+
# ╟
75+
self.parts["left_tee"]
76+
# ─────╫
77+
+ (
78+
self.parts["header_row_sep"] * self.cell_width
79+
+ self.parts["first_col_cross"]
80+
)
81+
# ───────────────────────
82+
+ (
83+
(
84+
self.parts["header_row_sep"] * self.cell_width
85+
+ self.parts["header_row_cross"]
86+
)
87+
* (cols - 1)
88+
)[0:-1]
89+
# ╢
90+
+ self.parts["right_tee"],
91+
]
92+
# add table body
93+
for p in self.body:
94+
# add table row
95+
table += [
96+
# ║
97+
self.parts["left_edge"]
98+
+
99+
# 1 ║
100+
f" {p[0]} "
101+
+ self.parts["first_col_sep"]
102+
# 40 40 40 40
103+
+ self.parts["middle_edge"].join(
104+
f"{p[i].rjust(4)} " for i in range(1, cols)
105+
)
106+
# ║
107+
+ self.parts["right_edge"]
108+
]
109+
# footer row
110+
table += [
111+
# ╟
112+
self.parts["left_tee"]
113+
# ─────╫
114+
+ (
115+
self.parts["footer_row_sep"] * self.cell_width
116+
+ self.parts["first_col_cross"]
117+
)
118+
# ───────────────────────
119+
+ (
120+
(
121+
self.parts["footer_row_sep"] * self.cell_width
122+
+ self.parts["footer_row_cross"]
123+
)
124+
* (cols - 1)
125+
)[0:-1]
126+
# ╢
127+
+ self.parts["right_tee"],
128+
# ║
129+
self.parts["left_edge"]
130+
# SUM ║
131+
+ f"{self.footer_row[0].rjust(4)} " + self.parts["first_col_sep"]
132+
# 120 ║ 120 ║ 120 ║ 120 ║
133+
+ self.parts["middle_edge"].join(
134+
f"{self.footer_row[i].rjust(4)} " for i in range(1, cols)
135+
)
136+
# ║
137+
+ self.parts["right_edge"],
138+
# ╚
139+
self.parts["bottom_left_corner"]
140+
# ═════╩
141+
+ self.parts["bottom_edge"] * self.cell_width
142+
+ self.parts["first_col_bottom_tee"]
143+
# ════════════════════════
144+
+ (
145+
(self.parts["bottom_edge"] * self.cell_width + self.parts["bottom_tee"])
146+
* (cols - 1)
147+
)[0:-1]
148+
# ╗
149+
+ self.parts["bottom_right_corner"],
150+
]
151+
return "\n".join(table)
152+
153+
154+
def table2ascii(
155+
header_row: List[str], body: List[List[str]], footer_row: List[str]
156+
) -> str:
157+
"""Convert a 2D Python table to ASCII text
158+
#TODO: add param documentation
159+
"""
160+
return TableToAscii(header_row, body, footer_row).to_ascii()

0 commit comments

Comments
 (0)