1
0
Fork 0
forked from itec/banana

Compare commits

..

12 commits

Author SHA1 Message Date
cf47edacd8 Merge branch 'feat/python-3.11-support' of arto/banana into master 2024-01-01 12:27:39 +01:00
aartoni
b51d652203
Support for Python 3.11 (current stable) 2023-09-09 23:10:30 +02:00
ce4d52ba4a Contributors 2020-12-26 00:48:50 +01:00
64d29bb27c Fixed idx for compatibility 2020-12-25 23:02:07 +01:00
32d30b7b2a Changed setup + small fixes 2020-12-19 23:58:44 +01:00
e8cef5fcf3 credits 2020-10-29 17:37:25 +01:00
c8e5be7de8 args aliases in cli 2020-10-29 16:58:27 +01:00
b20719c2fa removed preset from cli 2020-10-29 16:47:13 +01:00
17d3a0167d encode minlength 2020-10-29 16:34:28 +01:00
f18d1fdc68 translated 2020-10-29 16:23:12 +01:00
989f3820a7 renamed functions 2020-10-29 16:22:30 +01:00
aec04ae2c1 Saluti 2020-10-26 21:06:51 +01:00
11 changed files with 152 additions and 327 deletions

View file

@ -1,13 +1,13 @@
=======
Credits
=======
Development Lead
----------------
================
* itec <itec@ventuordici.org>
Contributors
------------
============
None yet. Why not be the first?
* Autoscatto
* Baku
* Boyska
* Encrypt
* Thegamer

View file

@ -9,14 +9,16 @@ banana
Features
--------
- Supports multiple, super-interesting bases: banana |:banana:|, ananas |:pineapple:|, ribes |:grapes:|, avocado |:avocado:|
- Supports multiple, super-interesting bases: banana 🍌, ananas 🍍, ribes 🍇 avocado 🥑
- Encode and decode easily
- Check validity of encoded words
- Generate random words |:game_die:|
- It's easy to support additional bases based on the same principles |:jigsaw:|
- Generate random words 🎲
- It's easy to support additional bases based on the same principles 🧩
Credits
-------
Thanks to Autoscatto, Baku, Boyska for suggestions and developement.
All hail Stefano Bartezzaghi
Special thanks to Stefano Bartezzaghi.
Loosely inspired by Stefano Bartezzaghi's book "Accavallavacca. Inventario di parole da gioco", Milano, Bompiani, 1992. ISBN 88-452-1948-8.

View file

@ -1,5 +1,5 @@
"""Top-level package for banana."""
from .bananalib import *
from .libbanana import *
__author__ = """itec"""
__email__ = "itec@ventuordici.org"

View file

@ -1,104 +0,0 @@
"""Main module."""
import logging
import random
log = logging.getLogger("bananalib")
class Codec:
def __init__(self, dictstart=0, shiftend=0, minlength=0, dictionary=None):
self.dictstart = dictstart
self.shiftend = shiftend
if dictionary is None:
self.dictionary = [list("bcdfglmnprstvz"), list("aeiou")]
else:
self.dictionary = dictionary
def encode(self, num, minlength=0):
dictionary = self.dictionary
numdict = len(dictionary)
v = num
st = ""
length = 0
idx = (numdict - 1 + self.dictstart + self.shiftend) % numdict
while not (
v == 0
and idx == (numdict - 1 + self.dictstart) % numdict
and length >= minlength
):
r = v % len(dictionary[idx])
v = int(v / len(dictionary[idx]))
st = dictionary[idx][r] + st
idx = (idx - 1) % numdict
length += 1
return st
def decode(self, word):
dictionary = self.dictionary
numdict = len(dictionary)
if (len(word) - self.shiftend) % numdict != 0:
raise ValueError("Banana non valida")
v = 0
for i in range(len(word)):
r = (numdict + i + self.dictstart) % numdict
try:
v = v * len(dictionary[r]) + dictionary[r].index(word[i])
except (ValueError, KeyError):
raise ValueError("Carattere non valido in posizione %d" % i + 1)
return v
def is_valid(self, word):
dictionary = self.dictionary
numdict = len(dictionary)
if (len(word) - self.shiftend) % numdict != 0:
return False
for i in range(len(word)):
r = (numdict + i + self.dictstart) % numdict
if word[i] not in dictionary[r]:
return False
return True
def random(self, minlength=6, prng=random.Random()):
numdict = len(self.dictionary)
word = ""
if minlength < 1:
return ""
curr_dict = (numdict - 1 + self.dictstart + self.shiftend) % numdict
final_dict = (numdict - 1 + self.dictstart) % numdict
while curr_dict != final_dict or len(word) < minlength:
word = prng.choice(self.dictionary[curr_dict]) + word
curr_dict = (curr_dict - 1) % numdict
return word
class BananaCodec(Codec):
def __init__(self):
super().__init__()
class RibesCodec(Codec):
def __init__(self):
super().__init__(0, 1)
class AnanasCodec(Codec):
def __init__(self):
super().__init__(1, 0)
class AvocadoCodec(Codec):
def __init__(self):
super().__init__(1, 1)
if __name__ == "__main__":
print("Ciao sono la libreria banana")

View file

@ -8,26 +8,22 @@ import banana
def get_codec(args):
if args.banana:
return banana.BananaCodec()
if args.ananas:
return banana.AnanasCodec()
if args.ribes:
return banana.RibesCodec()
if args.avocado:
return banana.AvocadoCodec()
kwargs = {}
if args.dictionary:
kwargs["dictionary"] = args.dictionary
if args.dictstart:
kwargs["dictstart"] = args.dictstart
if args.shiftend:
kwargs["shiftend"] = args.shiftend
if args.alphabets:
kwargs["alphabets"] = args.alphabets
if args.shiftalpha:
kwargs["shiftalpha"] = args.shiftalpha
if args.alphaend:
kwargs["alphaend"] = args.alphaend
return banana.Codec(**kwargs)
def main_encode(args):
print(get_codec(args).encode(args.num))
codec = get_codec(args)
kwargs = dict(num=args.num)
if args.minlength:
kwargs["minlength"] = args.minlength
print(codec.encode(**kwargs))
def main_decode(args):
@ -58,41 +54,38 @@ def colon_separated_list(s):
def main():
parser = argparse.ArgumentParser(description="Convert dec number to banana")
parser = argparse.ArgumentParser(description="Convert number to banana")
parser.add_argument(
"--log-level", choices=["DEBUG", "INFO", "WARN", "ERROR"], default="WARN"
)
parser.add_argument("--ananas", action="store_true")
parser.add_argument("--avocado", action="store_true")
parser.add_argument("--banana", action="store_true")
parser.add_argument("--ribes", action="store_true")
parser.add_argument(
"--dictionary",
help="Set dictionary in colon-separated list",
"--alphabets", "-a",
help="Set alphabets in colon-separated list",
type=colon_separated_list,
)
parser.add_argument(
"--dictstart", help="Set starting dictionary", type=int, default=0
"--shiftalpha", "-s", help="Set shift for alphabets", type=int, default=0
)
parser.add_argument(
"--shiftend", help="Set shift for ending dictionary", type=int, default=0
"--alphaend", "-e", help="Set ending alphabet", type=int, default=0
)
sub = parser.add_subparsers()
encode = sub.add_parser("encode", help="Convert numbers to words")
encode = sub.add_parser("encode", help="Convert number to word")
encode.add_argument("num", type=int)
encode.add_argument("--minlength", "-l", help="Set minimum length", type=int, default=1)
encode.set_defaults(func=main_encode)
decode = sub.add_parser("decode", help="Convert words to numbers")
decode = sub.add_parser("decode", help="Convert word to number")
decode.add_argument("word")
decode.set_defaults(func=main_decode)
check = sub.add_parser("check", help="Convert words to numbers")
check = sub.add_parser("check", help="Check if word is banana")
check.add_argument("word")
check.add_argument("--quiet", "-q", action="store_true")
check.set_defaults(func=main_check)
rand = sub.add_parser("random", help="Generate random banana")
rand.add_argument("--minlength", help="Set minimum length", type=int, default=6)
rand.add_argument("--minlength", "-l", help="Set minimum length", type=int, default=6)
rand.add_argument("--seed", type=int, default=None)
rand.set_defaults(func=main_random)

89
banana/libbanana.py Normal file
View file

@ -0,0 +1,89 @@
"""Main module."""
import logging
import random
log = logging.getLogger("libbanana")
class Codec:
def __init__(self, shiftalpha=0, alphaend=0, minlength=0, alphabets=None):
self.shiftalpha = shiftalpha
self.alphaend = alphaend
if alphabets is None:
self.alphabets = [list("bcdfglmnprstvz"), list("aeiou")]
else:
self.alphabets = alphabets
def encode(self, num, minlength=1):
alphabets = self.alphabets
numalpha = len(alphabets)
v = num
st = ""
length = 0
idx = (numalpha - 1 + self.shiftalpha + self.alphaend) % numalpha
while not (
v == 0
and idx == (numalpha - 1 + self.shiftalpha) % numalpha
and length >= minlength
):
r = v % len(alphabets[idx])
v = int(v / len(alphabets[idx]))
st = alphabets[idx][r] + st
idx = (idx + numalpha - 1) % numalpha
length += 1
return st
def decode(self, word):
alphabets = self.alphabets
numalpha = len(alphabets)
if (len(word) - self.alphaend) % numalpha != 0:
raise ValueError("Invalid banana")
v = 0
for i in range(len(word)):
r = (numalpha + i + self.shiftalpha) % numalpha
try:
v = v * len(alphabets[r]) + alphabets[r].index(word[i])
except (ValueError, KeyError):
raise ValueError("Invalid character in position %d" % i + 1)
return v
def is_valid(self, word):
alphabets = self.alphabets
numalpha = len(alphabets)
if (len(word) - self.alphaend) % numalpha != 0:
return False
for i in range(len(word)):
r = (numalpha + i + self.shiftalpha) % numalpha
if word[i] not in alphabets[r]:
return False
return True
def random(self, minlength=6, prng=random.Random()):
numalpha = len(self.alphabets)
word = ""
if minlength < 1:
return ""
curr_alpha = (numalpha - 1 + self.shiftalpha + self.alphaend) % numalpha
final_alpha = (numalpha - 1 + self.shiftalpha) % numalpha
while curr_alpha != final_alpha or len(word) < minlength:
word = prng.choice(self.alphabets[curr_alpha]) + word
curr_alpha = (curr_alpha - 1) % numalpha
return word
class BananaCodec(Codec):
def __init__(self):
super().__init__()
if __name__ == "__main__":
print("Hi I'm the basebanana library")

View file

@ -1,9 +1,9 @@
pip==19.2.3
pip==23.2.1
bump2version==0.5.11
wheel==0.33.6
watchdog==0.9.0
flake8==3.7.8
tox==3.14.0
tox==4.11.3
coverage==4.5.4
Sphinx==1.8.5
m2r==0.2.1
@ -11,5 +11,5 @@ twine==1.14.0
sphinxemoji==0.1.7
pytest==4.6.5
pytest==6.2.5
pytest-runner==5.1

View file

@ -22,5 +22,4 @@ exclude = docs
test = pytest
[tool:pytest]
collect_ignore = ['setup.py']
addopts = --ignore=setup.py

View file

@ -31,25 +31,24 @@ setup(
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
],
description="banana",
description="basebanana",
entry_points={
"console_scripts": [
"basebanana=banana.cli:main",
"banana=banana.cli:main",
"accavallavacca=banana.cli:main",
"bananarandom=banana.cli:bananarandom",
]
},
install_requires=requirements,
license="MIT license",
long_description=readme + "\n\n" + history,
include_package_data=True,
keywords="banana",
name="banana",
keywords="basebanana",
name="basebanana",
packages=find_packages(include=["banana", "banana.*"]),
setup_requires=setup_requirements,
test_suite="tests",
tests_require=test_requirements,
url="https://git.lattuga.net/itec/banana",
version="0.1.0",
version="0.2.0",
zip_safe=False,
)

View file

@ -5,9 +5,21 @@ import random
import pytest
from banana import AnanasCodec, AvocadoCodec, BananaCodec, RibesCodec
from banana import BananaCodec
banana_conversions = {"be": 1, "beba": 70, "zu": 69, "bezu": 139, "nana": 2485}
banana_conversions = {
"be": 1,
"da": 10,
"bema" : 100,
"duga": 1000,
"bibiva": 10000,
"galopa": 100000,
"bivucasa": 1000000,
"beba": 70,
"zu": 69,
"bezu": 139,
"nana": 2485,
}
@pytest.fixture(params=banana_conversions.items())
@ -15,74 +27,9 @@ def banana_known(request):
yield request.param
avocado_conversions = {
"a": 0,
"aca": 5,
"ada": 10,
"afa": 15,
"aga": 20,
"ala": 25,
"ama": 30,
"ana": 35,
"apa": 40,
"ara": 45,
"asa": 50,
"ata": 55,
"ava": 60,
"aza": 65,
"eba": 70,
"eca": 75,
"eda": 80,
"efa": 85,
"ega": 90,
}
@pytest.fixture(params=avocado_conversions.items())
def avocado_known(request):
yield request.param
ribes_conversions = {"b": 0, "c": 1, "z": 13, "beb": 14, "bec": 15}
@pytest.fixture(params=ribes_conversions.items())
def ribes_known(request):
yield request.param
ananas_conversions = {
"ac": 1,
"al": 5,
"as": 10,
"ec": 15,
"em": 20,
"et": 25,
"id": 30,
"in": 35,
"iv": 40,
"of": 45,
"op": 50,
"oz": 55,
"ug": 60,
"ur": 65,
"acab": 70,
"acal": 75,
"acas": 80,
"acec": 85,
"acem": 90,
}
ananas_codec = AnanasCodec()
avocado_codec = AvocadoCodec()
banana_codec = BananaCodec()
ribes_codec = RibesCodec()
@pytest.fixture(params=ananas_conversions.items())
def ananas_known(request):
yield request.param
def test_banana_to_dec_known(banana_known):
@ -99,12 +46,6 @@ def test_banana_is_banana(banana_known):
assert banana_codec.is_valid(banana_known[0])
def test_banana_is_only_banana(banana_known):
assert not ribes_codec.is_valid(banana_known[0])
assert not ananas_codec.is_valid(banana_known[0])
assert not avocado_codec.is_valid(banana_known[0])
def test_banana2dec_prefix_ba(banana_known):
"""un ba all'inizio non cambia nulla!"""
word, value = banana_known
@ -112,66 +53,6 @@ def test_banana2dec_prefix_ba(banana_known):
assert banana_codec.decode(prefix + word) == value
def test_ribes_to_dec_known(ribes_known):
word, value = ribes_known
assert ribes_codec.decode(word) == value
def test_dec_to_ribes_known(ribes_known):
word, value = ribes_known
assert ribes_codec.encode(value) == word
def test_ribes_is_ribes(ribes_known):
assert ribes_codec.is_valid(ribes_known[0])
def test_ribes_is_only_ribes(ribes_known):
assert not banana_codec.is_valid(ribes_known[0])
assert not ananas_codec.is_valid(ribes_known[0])
assert not avocado_codec.is_valid(ribes_known[0])
def test_avocado_to_dec_known(avocado_known):
word, value = avocado_known
assert avocado_codec.decode(word) == value
def test_dec_to_avocado_known(avocado_known):
word, value = avocado_known
assert avocado_codec.encode(value) == word
def test_avocado_is_avocado(avocado_known):
assert avocado_codec.is_valid(avocado_known[0])
def test_avocado_is_only_avocado(avocado_known):
assert not ribes_codec.is_valid(avocado_known[0])
assert not ananas_codec.is_valid(avocado_known[0])
assert not banana_codec.is_valid(avocado_known[0])
def test_ananas_to_dec_known(ananas_known):
word, value = ananas_known
assert ananas_codec.decode(word) == value
def test_dec_to_ananas_known(ananas_known):
word, value = ananas_known
assert ananas_codec.encode(value) == word
def test_ananas_is_ananas(ananas_known):
assert ananas_codec.is_valid(ananas_known[0])
def test_ananas_is_only_ananas(ananas_known):
assert not ribes_codec.is_valid(ananas_known[0])
assert not banana_codec.is_valid(ananas_known[0])
assert not banana_codec.is_valid(ananas_known[0])
def test_answer_to_life_the_universe_and_everything():
banana = banana_codec.decode("banana")
assert banana != 42
@ -180,41 +61,4 @@ def test_answer_to_life_the_universe_and_everything():
def test_random_len_0():
assert banana_codec.random(minlength=0) == ""
assert ananas_codec.random(minlength=0) == ""
assert avocado_codec.random(minlength=0) == ""
assert ribes_codec.random(minlength=0) == ""
def test_banana_random_minlength_even():
for l in (0, 2, 4, 6, 8, 10, 12):
assert len(banana_codec.random(minlength=l)) == l
assert len(ananas_codec.random(minlength=l)) == l
def test_banana_random_minlength_odd():
for l in (1, 3, 5, 7, 9):
assert len(banana_codec.random(minlength=l)) == l + 1
assert len(ananas_codec.random(minlength=l)) == l + 1
def test_ribes_random_minlength_even():
for l in (2, 4, 6, 8, 10, 12):
assert len(ribes_codec.random(minlength=l)) == l + 1
assert len(avocado_codec.random(minlength=l)) == l + 1
def test_ribes_random_minlength_odd():
for l in (1, 3, 5, 7, 9):
assert len(ribes_codec.random(minlength=l)) == l
assert len(avocado_codec.random(minlength=l)) == l
def test_random_coherence_please():
for codec in (ribes_codec, ananas_codec, avocado_codec, banana_codec):
for seed in range(30):
word = codec.random(prng=random.Random(seed))
assert codec.is_valid(word), "%s (seed %d) non valido per %s" % (
word,
seed,
codec.__class__.__name__,
)

View file

@ -1,8 +1,11 @@
[tox]
envlist = py35, py36, py37, py38, flake8
envlist = py35, py36, py37, py38, py39, py310, py311, flake8
[travis]
python =
3.11: py311
3.10: py310
3.9: py39
3.8: py38
3.7: py37
3.6: py36