# Copyright 2020 HTCondor Team, Computer Sciences Department,
# University of Wisconsin-Madison, WI.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import fnmatch
import random
import string
from pathlib import Path
from typing import Optional, Tuple
from htmap import exceptions, names, settings
def tags_dir() -> Path:
return Path(settings["HTMAP_DIR"]) / names.TAGS_DIR
def tag_file_path(tag: str) -> Path:
return tags_dir() / tag
def raise_if_tag_already_exists(tag: str) -> None:
"""Raise a :class:`htmap.exceptions.TagAlreadyExists` if the ``tag`` already exists."""
if tag_file_path(tag).exists():
raise exceptions.TagAlreadyExists(
f'The requested tag "{tag}" already exists. Load the Map with htmap.load("{tag}"), or remove it using htmap.remove("{tag}").'
)
INVALID_TAG_CHARACTERS = {
"/",
"\\", # backslash
"<",
">",
":",
'"',
"|",
"?",
"*",
" ",
"[",
"]",
"!",
}
def raise_if_tag_is_invalid(tag: str) -> None:
"""Raise a :class:`htmap.exceptions.InvalidTag` if the ``tag`` contains any invalid characters."""
if len(tag) < 1:
raise exceptions.InvalidTag("The tag must be a non-empty string")
invalid_chars = set(tag).intersection(INVALID_TAG_CHARACTERS)
if len(invalid_chars) != 0:
raise exceptions.InvalidTag(f"These characters in tag {tag} are not valid: {invalid_chars}")
ADJECTIVES = (
"angry",
"barbed",
"bland",
"blue",
"breezy",
"burst",
"busy",
"calm",
"clever",
"coy",
"curly",
"dark",
"deep",
"dire",
"dizzy",
"dry",
"fair",
"fancy",
"fierce",
"firm",
"fuzzy",
"gentle",
"green",
"happy",
"happy",
"harsh",
"hollow",
"husky",
"jaded",
"light",
"prim",
"proper",
"proud",
"puny",
"quick",
"rapid",
"rare",
"red",
"ripe",
"scratchy",
"shaky",
"shallow",
"short",
"sleek",
"slow",
"sly",
"snide",
"soft",
"soggy",
"super",
"sweet",
"swift",
"tall",
"tan",
"thick",
"thin",
"tiny",
"torrid",
"trite",
"twin",
"vast",
"wicked",
"wise",
)
NOUNS = (
"actor",
"apple",
"area",
"axe",
"badge",
"beak",
"bird",
"box",
"cake",
"car",
"cat",
"chair",
"city",
"coil",
"cookie",
"dog",
"drone",
"echo",
"exam",
"fact",
"farm",
"foot",
"frog",
"goal",
"hand",
"heel",
"idea",
"jaw",
"law",
"map",
"note",
"oven",
"poem",
"rig",
"ring",
"river",
"road",
"robe",
"rock",
"scone",
"sock",
"stick",
"stream",
"table",
"tooth",
"town",
"tub",
"year",
"zebra",
)
def random_tag() -> str:
existing_tags = set(get_tags())
for attempt in range(50):
adj1, adj2 = random.sample(ADJECTIVES, k=2)
noun = random.choice(NOUNS)
tag = f"{adj1}-{adj2}-{noun}"
if tag not in existing_tags:
return tag
options = string.ascii_letters + string.digits
for attempt in range(1_000_000):
tag = "".join(random.choices(options, k=6))
if tag not in existing_tags:
return tag
raise exceptions.InvalidTag(
"Could not find an unused random tag (try cleaning your transient maps)"
)