From 671c0e42b4167f4b7ff93e3b96922bf130c12718 Mon Sep 17 00:00:00 2001 From: Ryan Voots Date: Sun, 13 Nov 2022 13:39:41 -0500 Subject: [PATCH 01/59] Fix docker tmp/ and extensions/ handling for docker. might also work for symlinks --- modules/ui_extensions.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/modules/ui_extensions.py b/modules/ui_extensions.py index 6671cb60..95b63f24 100644 --- a/modules/ui_extensions.py +++ b/modules/ui_extensions.py @@ -9,6 +9,8 @@ import git import gradio as gr import html +import shutil +import errno from modules import extensions, shared, paths @@ -132,7 +134,18 @@ def install_extension_from_url(dirname, url): repo = git.Repo.clone_from(url, tmpdir) repo.remote().fetch() - os.rename(tmpdir, target_dir) + try: + os.rename(tmpdir, target_dir) + except OSError as err: + # TODO what does this do on windows? I think it'll be a different error code but I don't have a system to check it + # Shouldn't cause any new issues at least but we probably want to handle it there too. + if err.errno == errno.EXDEV: + # Cross device link, typical in docker or when tmp/ and extensions/ are on different file systems + # Since we can't use a rename, do the slower but more versitile shutil.move() + shutil.move(tmpdir, target_dir) + else: + # Something else, not enough free space, permissions, etc. rethrow it so that it gets handled. + raise(err) import launch launch.run_extension_installer(target_dir) From 9146a5884cbdf67c019685950f7ad0b3f7bd9230 Mon Sep 17 00:00:00 2001 From: uservar <63248296+uservar@users.noreply.github.com> Date: Sun, 27 Nov 2022 19:11:50 +0000 Subject: [PATCH 02/59] Better should hijack inpainting detection --- modules/sd_hijack_inpainting.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/sd_hijack_inpainting.py b/modules/sd_hijack_inpainting.py index 938f9a58..5dcbbed9 100644 --- a/modules/sd_hijack_inpainting.py +++ b/modules/sd_hijack_inpainting.py @@ -1,3 +1,4 @@ +import os import torch from einops import repeat @@ -319,7 +320,9 @@ class LatentInpaintDiffusion(LatentDiffusion): def should_hijack_inpainting(checkpoint_info): - return str(checkpoint_info.filename).endswith("inpainting.ckpt") and not checkpoint_info.config.endswith("inpainting.yaml") + ckpt_basename = os.path.basename(checkpoint_info.filename).lower() + cfg_basename = os.path.basename(checkpoint_info.config).lower() + return "inpainting" in ckpt_basename and not "inpainting" in cfg_basename def do_inpainting_hijack(): From 358a8628f6abb4ca1e1bfddf122687c6fb13be0c Mon Sep 17 00:00:00 2001 From: Andrew Ryan Date: Thu, 8 Dec 2022 07:09:09 +0000 Subject: [PATCH 03/59] Add latent upscale option to img2img Recently, the option to do latent upscale was added to txt2img highres fix. This feature runs by scaling the latent sample of the image, and then running a second pass of img2img. But, in this edition of highres fix, the image and parameters cannot be changed between the first pass and second pass. We might want to do a fixup in img2img before doing the second pass, or might want to run the second pass at a different resolution. This change adds the option for img2img to perform its upscale in latent space, rather than image space, giving very similar results to highres fix with latent upscale. The result is not exactly the same because there is an additional latent -> decoder -> image -> encoder -> latent that won't happen in highres fix, but this conversion has relatively small losses --- modules/processing.py | 6 +++++- modules/ui.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/processing.py b/modules/processing.py index 3d2c4dc9..ab5a34d0 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -795,7 +795,7 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): for img in self.init_images: image = img.convert("RGB") - if crop_region is None: + if crop_region is None and self.resize_mode != 3: image = images.resize_image(self.resize_mode, image, self.width, self.height) if image_mask is not None: @@ -804,6 +804,7 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): self.overlay_images.append(image_masked.convert('RGBA')) + # crop_region is not none iif we are doing inpaint full res if crop_region is not None: image = image.crop(crop_region) image = images.resize_image(2, image, self.width, self.height) @@ -840,6 +841,9 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): self.init_latent = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(image)) + if self.resize_mode == 3: + self.init_latent = torch.nn.functional.interpolate(self.init_latent, size=(self.height // opt_f, self.width // opt_f), mode="bilinear") + if image_mask is not None: init_mask = latent_mask latmask = init_mask.convert('RGB').resize((self.init_latent.shape[3], self.init_latent.shape[2])) diff --git a/modules/ui.py b/modules/ui.py index b2b8de90..fe4abe05 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -829,7 +829,7 @@ def create_ui(): img2img_batch_output_dir = gr.Textbox(label="Output directory", **shared.hide_dirs) with gr.Row(): - resize_mode = gr.Radio(label="Resize mode", elem_id="resize_mode", show_label=False, choices=["Just resize", "Crop and resize", "Resize and fill"], type="index", value="Just resize") + resize_mode = gr.Radio(label="Resize mode", elem_id="resize_mode", show_label=False, choices=["Just resize", "Crop and resize", "Resize and fill", "Upscale Latent Space"], type="index", value="Just resize") steps = gr.Slider(minimum=1, maximum=150, step=1, label="Sampling Steps", value=20) sampler_index = gr.Radio(label='Sampling method', choices=[x.name for x in samplers_for_img2img], value=samplers_for_img2img[0].name, type="index") From a1c8ad88283f7b3e861e4722c71e39bf71eec744 Mon Sep 17 00:00:00 2001 From: MrCheeze Date: Sat, 10 Dec 2022 11:02:47 -0500 Subject: [PATCH 04/59] unload depth model if medvram/lowvram enabled --- modules/lowvram.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/modules/lowvram.py b/modules/lowvram.py index aa464a95..042a0254 100644 --- a/modules/lowvram.py +++ b/modules/lowvram.py @@ -55,18 +55,20 @@ def setup_for_low_vram(sd_model, use_medvram): if hasattr(sd_model.cond_stage_model, 'model'): sd_model.cond_stage_model.transformer = sd_model.cond_stage_model.model - # remove three big modules, cond, first_stage, and unet from the model and then + # remove four big modules, cond, first_stage, depth (if applicable), and unet from the model and then # send the model to GPU. Then put modules back. the modules will be in CPU. - stored = sd_model.cond_stage_model.transformer, sd_model.first_stage_model, sd_model.model - sd_model.cond_stage_model.transformer, sd_model.first_stage_model, sd_model.model = None, None, None + stored = sd_model.cond_stage_model.transformer, sd_model.first_stage_model, getattr(sd_model, 'depth_model', None), sd_model.model + sd_model.cond_stage_model.transformer, sd_model.first_stage_model, sd_model.depth_model, sd_model.model = None, None, None, None sd_model.to(devices.device) - sd_model.cond_stage_model.transformer, sd_model.first_stage_model, sd_model.model = stored + sd_model.cond_stage_model.transformer, sd_model.first_stage_model, sd_model.depth_model, sd_model.model = stored - # register hooks for those the first two models + # register hooks for those the first three models sd_model.cond_stage_model.transformer.register_forward_pre_hook(send_me_to_gpu) sd_model.first_stage_model.register_forward_pre_hook(send_me_to_gpu) sd_model.first_stage_model.encode = first_stage_model_encode_wrap sd_model.first_stage_model.decode = first_stage_model_decode_wrap + if sd_model.depth_model: + sd_model.depth_model.register_forward_pre_hook(send_me_to_gpu) parents[sd_model.cond_stage_model.transformer] = sd_model.cond_stage_model if hasattr(sd_model.cond_stage_model, 'model'): From bd81a09eacf02dad095b98094ab936f276d0343f Mon Sep 17 00:00:00 2001 From: MrCheeze Date: Sat, 10 Dec 2022 11:29:26 -0500 Subject: [PATCH 05/59] fix support for 2.0 inpainting model while maintaining support for 1.5 inpainting model --- modules/sd_hijack_inpainting.py | 3 +-- modules/sd_models.py | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/sd_hijack_inpainting.py b/modules/sd_hijack_inpainting.py index 938f9a58..5018b047 100644 --- a/modules/sd_hijack_inpainting.py +++ b/modules/sd_hijack_inpainting.py @@ -324,12 +324,11 @@ def should_hijack_inpainting(checkpoint_info): def do_inpainting_hijack(): # most of this stuff seems to no longer be needed because it is already included into SD2.0 - # LatentInpaintDiffusion remains because SD2.0's LatentInpaintDiffusion can't be loaded without specifying a checkpoint # p_sample_plms is needed because PLMS can't work with dicts as conditionings # this file should be cleaned up later if weverything tuens out to work fine # ldm.models.diffusion.ddpm.get_unconditional_conditioning = get_unconditional_conditioning - ldm.models.diffusion.ddpm.LatentInpaintDiffusion = LatentInpaintDiffusion + # ldm.models.diffusion.ddpm.LatentInpaintDiffusion = LatentInpaintDiffusion # ldm.models.diffusion.ddim.DDIMSampler.p_sample_ddim = p_sample_ddim # ldm.models.diffusion.ddim.DDIMSampler.sample = sample_ddim diff --git a/modules/sd_models.py b/modules/sd_models.py index 5b37f3fe..b64f573f 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -296,6 +296,7 @@ def load_model(checkpoint_info=None): sd_config.model.params.use_ema = False sd_config.model.params.conditioning_key = "hybrid" sd_config.model.params.unet_config.params.in_channels = 9 + sd_config.model.params.finetune_keys = None # Create a "fake" config with a different name so that we know to unload it when switching models. checkpoint_info = checkpoint_info._replace(config=checkpoint_info.config.replace(".yaml", "-inpainting.yaml")) From 8bcdd50461090a2dd238082b33f4c1423378ebbd Mon Sep 17 00:00:00 2001 From: wywywywy Date: Sat, 10 Dec 2022 18:57:18 +0000 Subject: [PATCH 06/59] Add safetensors support to LDSR --- extensions-builtin/LDSR/ldsr_model_arch.py | 10 ++++++++-- extensions-builtin/LDSR/scripts/ldsr_model.py | 8 ++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/extensions-builtin/LDSR/ldsr_model_arch.py b/extensions-builtin/LDSR/ldsr_model_arch.py index 8b048ae0..f5bd8ae4 100644 --- a/extensions-builtin/LDSR/ldsr_model_arch.py +++ b/extensions-builtin/LDSR/ldsr_model_arch.py @@ -1,3 +1,4 @@ +import os import gc import time import warnings @@ -8,6 +9,7 @@ import torchvision from PIL import Image from einops import rearrange, repeat from omegaconf import OmegaConf +import safetensors.torch from ldm.models.diffusion.ddim import DDIMSampler from ldm.util import instantiate_from_config, ismap @@ -28,8 +30,12 @@ class LDSR: model: torch.nn.Module = cached_ldsr_model else: print(f"Loading model from {self.modelPath}") - pl_sd = torch.load(self.modelPath, map_location="cpu") - sd = pl_sd["state_dict"] + _, extension = os.path.splitext(self.modelPath) + if extension.lower() == ".safetensors": + pl_sd = safetensors.torch.load_file(self.modelPath, device="cpu") + else: + pl_sd = torch.load(self.modelPath, map_location="cpu") + sd = pl_sd["state_dict"] if "state_dict" in pl_sd else pl_sd config = OmegaConf.load(self.yamlPath) config.model.target = "ldm.models.diffusion.ddpm.LatentDiffusionV1" model: torch.nn.Module = instantiate_from_config(config.model) diff --git a/extensions-builtin/LDSR/scripts/ldsr_model.py b/extensions-builtin/LDSR/scripts/ldsr_model.py index 29d5f94e..b8cff29b 100644 --- a/extensions-builtin/LDSR/scripts/ldsr_model.py +++ b/extensions-builtin/LDSR/scripts/ldsr_model.py @@ -25,6 +25,7 @@ class UpscalerLDSR(Upscaler): yaml_path = os.path.join(self.model_path, "project.yaml") old_model_path = os.path.join(self.model_path, "model.pth") new_model_path = os.path.join(self.model_path, "model.ckpt") + safetensors_model_path = os.path.join(self.model_path, "model.safetensors") if os.path.exists(yaml_path): statinfo = os.stat(yaml_path) if statinfo.st_size >= 10485760: @@ -33,8 +34,11 @@ class UpscalerLDSR(Upscaler): if os.path.exists(old_model_path): print("Renaming model from model.pth to model.ckpt") os.rename(old_model_path, new_model_path) - model = load_file_from_url(url=self.model_url, model_dir=self.model_path, - file_name="model.ckpt", progress=True) + if os.path.exists(safetensors_model_path): + model = safetensors_model_path + else: + model = load_file_from_url(url=self.model_url, model_dir=self.model_path, + file_name="model.ckpt", progress=True) yaml = load_file_from_url(url=self.yaml_url, model_dir=self.model_path, file_name="project.yaml", progress=True) From 303df25cc287365b43ede22e0c082ae06aa452b2 Mon Sep 17 00:00:00 2001 From: Bwin4L Date: Sat, 10 Dec 2022 22:58:06 +0100 Subject: [PATCH 07/59] Make the generated image count only count new images in the currently active tab --- javascript/notification.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/notification.js b/javascript/notification.js index f96de313..040a3afa 100644 --- a/javascript/notification.js +++ b/javascript/notification.js @@ -15,7 +15,7 @@ onUiUpdate(function(){ } } - const galleryPreviews = gradioApp().querySelectorAll('img.h-full.w-full.overflow-hidden'); + const galleryPreviews = gradioApp().querySelectorAll('div[id^="tab_"][style*="display: block"] img.h-full.w-full.overflow-hidden'); if (galleryPreviews == null) return; From 7b0a28f8ee937048f594211169565a361a692d2a Mon Sep 17 00:00:00 2001 From: "Alex \"mcmonkey\" Goodwin" Date: Sun, 11 Dec 2022 02:17:14 -0800 Subject: [PATCH 08/59] fix hints file typo --- javascript/hints.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/hints.js b/javascript/hints.js index 47e24616..28e1651b 100644 --- a/javascript/hints.js +++ b/javascript/hints.js @@ -17,7 +17,7 @@ titles = { "\u2199\ufe0f": "Read generation parameters from prompt or last generation if prompt is empty into user interface.", "\u{1f4c2}": "Open images output directory", "\u{1f4be}": "Save style", - "\U0001F5D1": "Clear prompt" + "\U0001F5D1": "Clear prompt", "\u{1f4cb}": "Apply selected styles to current prompt", "Inpaint a part of image": "Draw a mask over an image, and the script will regenerate the masked area with content according to prompt", From d5a9de16620869e0e5521a0a07d58f226ed661af Mon Sep 17 00:00:00 2001 From: repligator <114337591+repligator@users.noreply.github.com> Date: Sun, 11 Dec 2022 09:39:56 -0500 Subject: [PATCH 09/59] Update hints.js - DPM adaptive Added mouse-over for DPM adaptive --- javascript/hints.js | 1 + 1 file changed, 1 insertion(+) diff --git a/javascript/hints.js b/javascript/hints.js index 47e24616..ab087530 100644 --- a/javascript/hints.js +++ b/javascript/hints.js @@ -6,6 +6,7 @@ titles = { "GFPGAN": "Restore low quality faces using GFPGAN neural network", "Euler a": "Euler Ancestral - very creative, each can get a completely different picture depending on step count, setting steps to higher than 30-40 does not help", "DDIM": "Denoising Diffusion Implicit Models - best at inpainting", + "DPM adaptive": "Ignores step count - uses a number of steps determined by the CFG and resolution", "Batch count": "How many batches of images to create", "Batch size": "How many image to create in a single batch", From 59c6511494c55a578eecdf71fb4590b6bd5d04a7 Mon Sep 17 00:00:00 2001 From: Dean van Dugteren <31391056+deanpress@users.noreply.github.com> Date: Sun, 11 Dec 2022 17:08:51 +0100 Subject: [PATCH 10/59] fix: fallback model_checkpoint if it's empty This fixes the following error when SD attempts to start with a deleted checkpoint: ``` Traceback (most recent call last): File "D:\Web\stable-diffusion-webui\launch.py", line 295, in start() File "D:\Web\stable-diffusion-webui\launch.py", line 290, in start webui.webui() File "D:\Web\stable-diffusion-webui\webui.py", line 132, in webui initialize() File "D:\Web\stable-diffusion-webui\webui.py", line 62, in initialize modules.sd_models.load_model() File "D:\Web\stable-diffusion-webui\modules\sd_models.py", line 283, in load_model checkpoint_info = checkpoint_info or select_checkpoint() File "D:\Web\stable-diffusion-webui\modules\sd_models.py", line 117, in select_checkpoint checkpoint_info = checkpoints_list.get(model_checkpoint, None) TypeError: unhashable type: 'list' ``` --- modules/sd_models.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/sd_models.py b/modules/sd_models.py index 5b37f3fe..b6d75db7 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -111,6 +111,10 @@ def model_hash(filename): def select_checkpoint(): model_checkpoint = shared.opts.sd_model_checkpoint + + if len(model_checkpoint) == 0: + model_checkpoint = shared.default_sd_model_file + checkpoint_info = checkpoints_list.get(model_checkpoint, None) if checkpoint_info is not None: return checkpoint_info From ec0a48826fb41c1b1baab45a9030f7eb55568fd0 Mon Sep 17 00:00:00 2001 From: MrCheeze Date: Sun, 11 Dec 2022 10:19:46 -0500 Subject: [PATCH 11/59] unconditionally set use_ema=False if value not specified (True never worked, and all configs except v1-inpainting-inference.yaml already correctly set it to False) --- modules/sd_models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/sd_models.py b/modules/sd_models.py index b64f573f..f36b299f 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -293,7 +293,6 @@ def load_model(checkpoint_info=None): if should_hijack_inpainting(checkpoint_info): # Hardcoded config for now... sd_config.model.target = "ldm.models.diffusion.ddpm.LatentInpaintDiffusion" - sd_config.model.params.use_ema = False sd_config.model.params.conditioning_key = "hybrid" sd_config.model.params.unet_config.params.in_channels = 9 sd_config.model.params.finetune_keys = None @@ -301,6 +300,9 @@ def load_model(checkpoint_info=None): # Create a "fake" config with a different name so that we know to unload it when switching models. checkpoint_info = checkpoint_info._replace(config=checkpoint_info.config.replace(".yaml", "-inpainting.yaml")) + if not hasattr(sd_config.model.params, "use_ema"): + sd_config.model.params.use_ema = False + do_inpainting_hijack() if shared.cmd_opts.no_half: From 960293d6b24f380f5744c94c9a46acaae6cc8c04 Mon Sep 17 00:00:00 2001 From: Dean Hopkins Date: Sun, 11 Dec 2022 19:16:44 +0000 Subject: [PATCH 12/59] API endpoint to refresh checkpoints API endpoint to refresh checkpoints --- modules/api/api.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/api/api.py b/modules/api/api.py index 89935a70..14d0baaa 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -96,6 +96,7 @@ class Api: self.add_api_route("/sdapi/v1/prompt-styles", self.get_promp_styles, methods=["GET"], response_model=List[PromptStyleItem]) self.add_api_route("/sdapi/v1/artist-categories", self.get_artists_categories, methods=["GET"], response_model=List[str]) self.add_api_route("/sdapi/v1/artists", self.get_artists, methods=["GET"], response_model=List[ArtistItem]) + self.add_api_route("/sdapi/v1/refresh-checkpoints", self.refresh_checkpoints, methods=["POST"]) def add_api_route(self, path: str, endpoint, **kwargs): if shared.cmd_opts.api_auth: @@ -321,6 +322,9 @@ class Api: def get_artists(self): return [{"name":x[0], "score":x[1], "category":x[2]} for x in shared.artist_db.artists] + + def refresh_checkpoints(self): + shared.refresh_checkpoints() def launch(self, server_name, port): self.app.include_router(self.router) From 00ca6a6db4674713a10d1de312559cb924ed3c55 Mon Sep 17 00:00:00 2001 From: ThereforeGames <95403634+ThereforeGames@users.noreply.github.com> Date: Sun, 11 Dec 2022 17:59:59 -0500 Subject: [PATCH 13/59] Add files via upload --- venv/Lib/site-packages/aenum/CHANGES | 489 ++ venv/Lib/site-packages/aenum/LICENSE | 32 + venv/Lib/site-packages/aenum/__init__.py | 4111 ++++++++++ .../aenum/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 104964 bytes .../aenum/__pycache__/_py3.cpython-39.pyc | Bin 0 -> 686 bytes venv/Lib/site-packages/aenum/_py2.py | 7 + venv/Lib/site-packages/aenum/_py3.py | 12 + venv/Lib/site-packages/aenum/doc/aenum.rst | 1568 ++++ venv/Lib/site-packages/aenum/test.py | 6832 +++++++++++++++++ venv/Lib/site-packages/aenum/test_v3.py | 1982 +++++ venv/Lib/site-packages/blendmodes/__init__.py | 2 + .../__pycache__/__init__.cpython-39.pyc | Bin 0 -> 285 bytes .../__pycache__/blend.cpython-39.pyc | Bin 0 -> 13302 bytes .../__pycache__/blendtype.cpython-39.pyc | Bin 0 -> 2850 bytes venv/Lib/site-packages/blendmodes/blend.py | 511 ++ .../Lib/site-packages/blendmodes/blendtype.py | 72 + .../site-packages/blendmodes/imagetools.py | 48 + 17 files changed, 15666 insertions(+) create mode 100644 venv/Lib/site-packages/aenum/CHANGES create mode 100644 venv/Lib/site-packages/aenum/LICENSE create mode 100644 venv/Lib/site-packages/aenum/__init__.py create mode 100644 venv/Lib/site-packages/aenum/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/aenum/__pycache__/_py3.cpython-39.pyc create mode 100644 venv/Lib/site-packages/aenum/_py2.py create mode 100644 venv/Lib/site-packages/aenum/_py3.py create mode 100644 venv/Lib/site-packages/aenum/doc/aenum.rst create mode 100644 venv/Lib/site-packages/aenum/test.py create mode 100644 venv/Lib/site-packages/aenum/test_v3.py create mode 100644 venv/Lib/site-packages/blendmodes/__init__.py create mode 100644 venv/Lib/site-packages/blendmodes/__pycache__/__init__.cpython-39.pyc create mode 100644 venv/Lib/site-packages/blendmodes/__pycache__/blend.cpython-39.pyc create mode 100644 venv/Lib/site-packages/blendmodes/__pycache__/blendtype.cpython-39.pyc create mode 100644 venv/Lib/site-packages/blendmodes/blend.py create mode 100644 venv/Lib/site-packages/blendmodes/blendtype.py create mode 100644 venv/Lib/site-packages/blendmodes/imagetools.py diff --git a/venv/Lib/site-packages/aenum/CHANGES b/venv/Lib/site-packages/aenum/CHANGES new file mode 100644 index 00000000..e8799f6d --- /dev/null +++ b/venv/Lib/site-packages/aenum/CHANGES @@ -0,0 +1,489 @@ +3.1.8 +===== + +recalculate bits used after all flags created (sometimes needed when a custom +`__new__` is in place. + + +3.1.7 +===== + +update flag creation to (possibly) add bitwise operator methods to newly +created flags + +update extend_enum() to work with 3.11 flags + + +3.1.6 +===== + +Update `dir()` on mixed enums to include mixed data type methods and +attributes. + +Rename `enum_property` to `property` to match stdlib. Recommended usage is +`aenum.property` (prefix with module name). + +Remove quadritic creation behavior. + + +BREAKING CHANGE BUG FIX that won't affect most people + +Enums with a custom `__new__` that: + +- use the enum machinery to generate the values; AND +- have keyword arguments set to a default (like `None`) + +will fail to generate a missing value. To fix: remove the default value and +instead specify it on the member creation line. + +BREAKING CHANGE + +In Python 3.11 the `str()` of mixed enums will now match its `format()` which +will be the normal `str()` of the data type -- so for an IntEnum you'll see +`5` instead of `Perm.R|X`. This affects IntEnum, StrEnum, and IntFlag. + + +3.1.5 +===== + +fix support of `auto()` kwds + + +3.1.3 +===== + +rename `aenum.property` to `aenum.enum_property` + +fix `enum_property` to work with `_init_` attributes + + +3.1.2 +===== + +fix `extend_enum()` for unhashable values + + +3.1.1 +===== + +fix `extend_enum()` for most cases + + +3.1.0 +===== + +AddValue is similar to the old AutoNumber: it will always activate, but +uses _generate_next_value_ to get the next value (so the user has some +control over the return data type instead of always getting an int). + + +BREAKING CHANGES + +AutoValue is gone. It was superflous and its removal simplified the code. +Simply put the fields needed in an `_init_` and `_generate_next_value_` +will be called to supply the missing values (this is probably already what +is happening). + + + +3.0.0 +===== + +standard Enum usage is unchanged + +BREAKING CHANGES + +Enum +- the more esoteric method of creating Enums have been modified or removed + - AutoNumber setting is gone, inherit from AutoNumberEnum instead + - creating members without specifying anything is removed (if you don't + know what this means, you weren't doing it) + +Flag +- unique flags are canonical (i.e. flags with powers of two values such as + 1, 2, 4, 8, 16, etc.) +- non-unique flags are aliases (i.e. values such as 3 or 7) +- iteration of Flag and flag members only uses canonical flags + + +ENHANCEMENTS + +Member creation has been redone to match Python 3.10's methods. This also +allows all supported Pythons (2.7, 3.3+) to use the __set_name__ and +__init_subclass__ protocols (more robustly than in aenum 2.2.5) + + +CHANGES + +enum_property() has been renamed to property() (old name still available, but +deprecated). + +bin() replacement shows negative integers in twos-complement + + + + +2.2.5 +===== + +call __init_subclass__ after members have been added, and in Pythons < 3.6 +call __set_name__ in Pythons < 3.6 +do not convert/disallow private names +add iteration/len support to NamedConstant + + +2.2.4 +===== + +add support to Constant to retrieve members by value + + --> class K(Constant): + ... one = 1 + ... two = 2 + + --> K.one + + + --> K(1) + + +add pickle/deepcopy support to Constant + +add support for Constant to use other Constant values + (resulting members /are not/ the same) + + --> class C(Constant) + ... one = K.one + ... three = 3 + + --> C.one == K.one + True + + --> C.one is K.one + False + +AutoNumber and auto() now work together + +Enum members are now added to the class as enum_property, which supports +unshadowing of parent class attributes when called on an Enum member: + + --> class StrEnum(str, Enum): + ... lower = 'lower' + ... upper = 'upper' + ... mixed = 'mixed' + + --> StrEnum.lower + + + --> StrEnum.lower.upper() + 'LOWER' + + --> StrEnum.upper + + + --> StrEnum.upper.upper() + 'UPPER' + + +2.2.3 +===== + +use members' type's methods __str__, __repr__, __format__, and +__reduce_ex__ if directly assigned in Enum class body; i.e.: + + --> class Color(str, Enum): + ... red = 'red' + ... green = 'green' + ... blue = 'blue' + ... __str__ = str.__str__ + + --> print(repr(Color.green)) + + + --> print(Color.green) + green + + +2.2.2 +===== + +replace _RouteClassAttributeToGetattr with enum_property (it is still +available as an alias) + +support constant() and auto() being used together: + + --> class Fruit(Flag): + ... _order_ = 'apple banana lemon orange' + ... apple = auto() + ... banana = auto() + ... lemon = auto() + ... orange = auto() + ... CitrusTypes = constant(lemon | orange) + + --> list(Fruit) + [Fruit.apple, Fruit.banana, Fruit.lemon, Fruit.orange] + + --> list(Fruit.CitrusTypes) + [Fruit.orange, Fruit.lemon] + + --> Fruit.orange in Fruit.CitrusTypes + True + + +2.2.1 +===== + +allow Enums to be called without a value + + class Color(Enum): + black = 0 + red = 1 + green = 2 + blue = 3 + # + @classmethod + def _missing_value_(cls, value): + if value is no_arg: + return cls.black + + >>> Color() + + +allow Enum name use while constructing Enum (Python 3.4+ only) + + --> class Color(Enum): + ... _order_ = 'BLACK WHITE' + ... BLACK = Color('black', '#000') + ... WHITE = Color('white', '#fff') + ... # + ... def __init__(self, label, hex): + ... self.label = label + ... self.hex = hex + + +2.2.0 +===== + +BREAKING CHANGE +--------------- +In Python 3+ classes defined inside an Enum no longer become members by +default; in Python 2 they still become members, but see below. + +For cross-compatibility and full control two decorators are provided: + +- @member --> forces item to become a member +- @nonmember --> excludes item from becoming a member + +So to have an Enum that behaves the same in Python 2 and 3, use the +decorators (and other compatibility shims): + + class Color(Enum): + + _order_ = 'red green blue' + + red = 1 + green = 2 + blue = 3 + + @nonmember + class Shades(Enum): + + _order_ = 'light medium dark' + + light = 1 + medium = 2 + dark = 3 + + +2.1.4 +===== + +EnumMeta: +- change __member_new__ to __new_member__ (as the stdlib enum does) +- assign member name to enum() instances (an Enum helper for defining members) +- handle empty iterables when using functional API +- make auto() work with previous enum members +- keep searching mixins until base class is found + +Enum: +- fix bug in Flag checks (ensure it is a Flag before checking the name) +- add multiple mixin support +- do not allow blank names (functional API) +- raise TypeError if _missing_* returns wrong type +- fix __format__ to honor custom __str__ + +extend_enum: +- support stdlib Enums +- use _generate_next_value_ if value not provided + +general: +- standardize exception formatting +- use getfullargspec() in Python 3 (avoids deprecation warnings) + + +2.1.2 +===== + +when order is callable, save it for subclass use + + +2.1.1 +===== + +correctly raise TypeError for non-Enum containment checks +support combining names with | for Flag key access +support _order_ being a callable + + +2.1.0 +===== + +support Flags being combined with other data types: +- add _create_pseudo_member_values_ +- add default __new__ and temporary _init_ + + +2.0.10 +====== + +ensure _ignore_ is set when _settings_ specified in body which includes +AutoValue + +make Flag members iterable + + +2.0.9 +===== + +fix missing comma in __all__ +fix extend_enum with custom __new__ methods +fix MultiValue with AutoNumber without _init_ + + +2.0.8 +===== + +extend_enum now handles aliases and multivalues correctly + + +2.0.7 +===== + +support mixin types with extend_enum +init and AutoNumber can now work together +add test for new Enum using EnumMeta +add tests for variations of multivalue and init +prevent deletion of NamedConstant.constant + + +2.0.6 +===== + +constants cannot be deleted (they already couldn't be changed) +constants can be used to define other constants + + +2.0.5 +===== + +_init_ and MultiValue can now work together + + +2.0.4 +===== + +_init_ and AutoValue (and _generate_next_value_) can now work together to +supply missing values even when some of the required values per member are +absent + + +2.0.3 +===== + +add _missing_value_ and _missing_name_ methods, deprecate _missing_ +make enum instances comparable + + +2.0.2 +===== + +both EnumMeta.__getattr__ and Enum.__new__ fall back to _missing_ + + +2.0.1 +===== + +auto() now works with other data types +AutoNumber supports legacy Enums (fixed regression) + + +2.0.0 +===== + +Flag and IntFlag added. + + +1.4.7 +===== + +fix %-interpolation bug +defined SqlLiteEnum only if sqlite exists +support pyflakes + + +1.4.6 +===== + +version numbering error + + +1.4.5 +===== + +revert AutoNumberEnum to custom __new__ instead of AutoNumber +use _ignore_ to shield against AutoNumber magic +inherit start and init settings from base Enums + + +1.4.4 +===== + +enabled export as a decorator +enabled _order_ to replace __order__ +enabled python2 support for settings, init, and start + + +1.4.3 +===== + +support _ignore_ for dynamically creating class bodies + + +1.4.2 +===== + +MultiValue, NoAlias, Unique, and init now work with Python 2 + + +1.4.1 +===== + +Py3: added Enum creation flags: Auto, MultiValue, NoAlias, Unique + +fixed extend_enum to honor Enum flags + + +1.4.0 +===== + +When possible aenum inherits from Python's own enum. + +Breaking change: enum members now default to evaluating as True to maintain +compatibility with the stdlib. + +Add your own __bool__ (__nonzero__ in Python 2) if need this behavior: + + def __bool__(self): + return bool(self.value) + __nonzero__ = __bool__ + diff --git a/venv/Lib/site-packages/aenum/LICENSE b/venv/Lib/site-packages/aenum/LICENSE new file mode 100644 index 00000000..b20361ce --- /dev/null +++ b/venv/Lib/site-packages/aenum/LICENSE @@ -0,0 +1,32 @@ +Copyright (c) 2015, 2016, 2017, 2018 Ethan Furman. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + + Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + Neither the name Ethan Furman nor the names of any + contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/aenum/__init__.py b/venv/Lib/site-packages/aenum/__init__.py new file mode 100644 index 00000000..26c504cc --- /dev/null +++ b/venv/Lib/site-packages/aenum/__init__.py @@ -0,0 +1,4111 @@ +"""Python Advanced Enumerations & NameTuples""" +from __future__ import print_function + +# imports +import sys as _sys +pyver = _sys.version_info[:2] +PY2 = pyver < (3, ) +PY3 = pyver >= (3, ) +PY2_6 = (2, 6) +PY3_3 = (3, 3) +PY3_4 = (3, 4) +PY3_5 = (3, 5) +PY3_6 = (3, 6) +PY3_11 = (3, 11) + +import re + +_bltin_property = property +_bltin_bin = bin + +try: + from collections import OrderedDict +except ImportError: + OrderedDict = dict +from collections import defaultdict +try: + import sqlite3 +except ImportError: + sqlite3 = None + +try: + RecursionError +except NameError: + # python3.4 + RecursionError = RuntimeError + +from operator import or_ as _or_, and_ as _and_, xor as _xor_, inv as _inv_ +from operator import abs as _abs_, add as _add_, floordiv as _floordiv_ +from operator import lshift as _lshift_, rshift as _rshift_, mod as _mod_ +from operator import mul as _mul_, neg as _neg_, pos as _pos_, pow as _pow_ +from operator import truediv as _truediv_, sub as _sub_ + +if PY2: + from ._py2 import * +if PY3: + from ._py3 import * + +obj_type = type + +__all__ = [ + 'NamedConstant', 'constant', 'skip', 'nonmember', 'member', 'no_arg', + 'Enum', 'IntEnum', 'AutoNumberEnum', 'OrderedEnum', 'UniqueEnum', + 'StrEnum', 'UpperStrEnum', 'LowerStrEnum', + 'Flag', 'IntFlag', + 'AddValue', 'MagicValue', 'MultiValue', 'NoAlias', 'Unique', + 'AddValueEnum', 'MultiValueEnum', 'NoAliasEnum', + 'enum', 'extend_enum', 'unique', 'property', + 'NamedTuple', 'SqliteEnum', + 'FlagBoundary', 'STRICT', 'CONFORM', 'EJECT', 'KEEP', + 'add_stdlib_integration', 'remove_stdlib_integration' + ] + +if sqlite3 is None: + __all__.remove('SqliteEnum') + +version = 3, 1, 12, 1 + +# shims +try: + any +except NameError: + def any(iterable): + for element in iterable: + if element: + return True + return False + +try: + unicode + unicode = unicode +except NameError: + # In Python 3 unicode no longer exists (it's just str) + unicode = str + +try: + basestring + basestring = bytes, unicode +except NameError: + # In Python 2 basestring is the ancestor of both str and unicode + # in Python 3 it's just str, but was missing in 3.1 + basestring = str, + +try: + long + baseinteger = int, long +except NameError: + baseinteger = int, + long = int +# deprecated +baseint = baseinteger + +try: + NoneType +except NameError: + NoneType = type(None) + +try: + # derive from stdlib enum if possible + import enum + if hasattr(enum, 'version'): + raise ImportError('wrong version') + else: + from enum import EnumMeta as StdlibEnumMeta, Enum as StdlibEnum, IntEnum as StdlibIntEnum + StdlibFlag = StdlibIntFlag = StdlibStrEnum = StdlibReprEnum = None +except ImportError: + StdlibEnumMeta = StdlibEnum = StdlibIntEnum = StdlibIntFlag = StdlibFlag = StdlibStrEnum = None + +if StdlibEnum: + try: + from enum import IntFlag as StdlibIntFlag, Flag as StdlibFlag + except ImportError: + pass + try: + from enum import StrEnum as StdlibStrEnum + except ImportError: + pass + try: + from enum import ReprEnum as StdlibReprEnum + except ImportError: + pass + + + + +# helpers +# will be exported later +MagicValue = AddValue = MultiValue = NoAlias = Unique = None + +def _bit_count(num): + """ + return number of set bits + + Counting bits set, Brian Kernighan's way* + + unsigned int v; // count the number of bits set in v + unsigned int c; // c accumulates the total bits set in v + for (c = 0; v; c++) + { v &= v - 1; } //clear the least significant bit set + + This method goes through as many iterations as there are set bits. So if we + have a 32-bit word with only the high bit set, then it will only go once + through the loop. + + * The C Programming Language 2nd Ed., Kernighan & Ritchie, 1988. + + This works because each subtraction "borrows" from the lowest 1-bit. For + example: + + loop pass 1 loop pass 2 + ----------- ----------- + 101000 100000 + - 1 - 1 + = 100111 = 011111 + & 101000 & 100000 + = 100000 = 0 + + It is an excellent technique for Python, since the size of the integer need + not be determined beforehand. + + (from https://wiki.python.org/moin/BitManipulation) + """ + count = 0 + while num: + num &= num - 1 + count += 1 + return count + +def _is_single_bit(value): + """ + True if only one bit set in value (should be an int) + """ + if value == 0: + return False + value &= value - 1 + return value == 0 + +def _iter_bits_lsb(value): + """ + Return each bit value one at a time. + + >>> list(_iter_bits_lsb(6)) + [2, 4] + """ + + while value: + bit = value & (~value + 1) + yield bit + value ^= bit + +def bin(value, max_bits=None): + """ + Like built-in bin(), except negative values are represented in + twos-compliment, and the leading bit always indicates sign + (0=positive, 1=negative). + + >>> bin(10) + '0b0 1010' + >>> bin(~10) # ~10 is -11 + '0b1 0101' + """ + + ceiling = 2 ** (value).bit_length() + if value >= 0: + s = _bltin_bin(value + ceiling).replace('1', '0', 1) + else: + s = _bltin_bin(~value ^ (ceiling - 1) + ceiling) + sign = s[:3] + digits = s[3:] + if max_bits is not None: + if len(digits) < max_bits: + digits = (sign[-1] * max_bits + digits)[-max_bits:] + return "%s %s" % (sign, digits) + + +try: + from types import DynamicClassAttribute + base = DynamicClassAttribute +except ImportError: + base = object + DynamicClassAttribute = None + +class property(base): + """ + This is a descriptor, used to define attributes that act differently + when accessed through an enum member and through an enum class. + Instance access is the same as property(), but access to an attribute + through the enum class will look in the class' _member_map_. + """ + + # inherit from DynamicClassAttribute if we can in order to get `inspect` + # support + + def __init__(self, fget=None, fset=None, fdel=None, doc=None): + self.fget = fget + self.fset = fset + self.fdel = fdel + # next two lines make property act the same as _bltin_property + self.__doc__ = doc or fget.__doc__ + self.overwrite_doc = doc is None + # support for abstract methods + self.__isabstractmethod__ = bool(getattr(fget, '__isabstractmethod__', False)) + # names, if possible + + def getter(self, fget): + fdoc = fget.__doc__ if self.overwrite_doc else None + result = type(self)(fget, self.fset, self.fdel, fdoc or self.__doc__) + result.overwrite_doc = self.__doc__ is None + return result + + def setter(self, fset): + fdoc = fget.__doc__ if self.overwrite_doc else None + result = type(self)(self.fget, fset, self.fdel, self.__doc__) + result.overwrite_doc = self.__doc__ is None + return result + + def deleter(self, fdel): + fdoc = fget.__doc__ if self.overwrite_doc else None + result = type(self)(self.fget, self.fset, fdel, self.__doc__) + result.overwrite_doc = self.__doc__ is None + return result + + def __repr__(self): + member = self.ownerclass._member_map_.get(self.name) + func = self.fget or self.fset or self.fdel + strings = [] + if member: + strings.append('%r' % member) + if func: + strings.append('function=%s' % func.__name__) + return 'property(%s)' % ', '.join(strings) + + def __get__(self, instance, ownerclass=None): + if instance is None: + try: + return ownerclass._member_map_[self.name] + except KeyError: + raise AttributeError( + '%r has no attribute %r' % (ownerclass, self.name) + ) + else: + if self.fget is not None: + return self.fget(instance) + else: + if self.fset is not None: + raise AttributeError( + 'cannot read attribute %r on %r' % (self.name, ownerclass) + ) + else: + try: + return instance.__dict__[self.name] + except KeyError: + raise AttributeError( + '%r member has no attribute %r' % (ownerclass, self.name) + ) + + def __set__(self, instance, value): + if self.fset is None: + if self.fget is not None: + raise AttributeError( + "cannot set attribute %r on " % (self.name, self.clsname) + ) + else: + instance.__dict__[self.name] = value + else: + return self.fset(instance, value) + + def __delete__(self, instance): + if self.fdel is None: + if self.fget or self.fset: + raise AttributeError( + "cannot delete attribute %r on " % (self.name, self.clsname) + ) + elif self.name in instance.__dict__: + del instance.__dict__[self.name] + else: + raise AttributeError( + "no attribute %r on member" % (self.name, self.clsname) + ) + else: + return self.fdel(instance) + + def __set_name__(self, ownerclass, name): + self.name = name + self.clsname = ownerclass.__name__ + self.ownerclass = ownerclass + +_RouteClassAttributeToGetattr = property +if DynamicClassAttribute is None: + DynamicClassAttribute = property +# deprecated +enum_property = property + +class NonMember(object): + """ + Protects item from becaming an Enum member during class creation. + """ + def __init__(self, value): + self.value = value + + def __get__(self, instance, ownerclass=None): + return self.value +skip = nonmember = NonMember + +class Member(object): + """ + Forces item to became an Enum member during class creation. + """ + def __init__(self, value): + self.value = value +member = Member + +class SentinelType(type): + def __repr__(cls): + return '<%s>' % cls.__name__ +Sentinel = SentinelType('Sentinel', (object, ), {}) + +def _is_descriptor(obj): + """Returns True if obj is a descriptor, False otherwise.""" + return ( + hasattr(obj, '__get__') or + hasattr(obj, '__set__') or + hasattr(obj, '__delete__')) + + +def _is_dunder(name): + """Returns True if a __dunder__ name, False otherwise.""" + return (len(name) > 4 and + name[:2] == name[-2:] == '__' and + name[2] != '_' and + name[-3] != '_') + + +def _is_sunder(name): + """Returns True if a _sunder_ name, False otherwise.""" + return (len(name) > 2 and + name[0] == name[-1] == '_' and + name[1] != '_' and + name[-2] != '_') + +def _is_internal_class(cls_name, obj): + # only 3.3 and up, always return False in 3.2 and below + if pyver < PY3_3: + return False + else: + qualname = getattr(obj, '__qualname__', False) + return not _is_descriptor(obj) and qualname and re.search(r"\.?%s\.\w+$" % cls_name, qualname) + +def _is_private_name(cls_name, name): + pattern = r'^_%s__\w+[^_]_?$' % (cls_name, ) + return re.search(pattern, name) + +def _power_of_two(value): + if value < 1: + return False + return value == 2 ** _high_bit(value) + +def bits(num): + if num in (0, 1): + return str(num) + negative = False + if num < 0: + negative = True + num = ~num + result = bits(num>>1) + str(num&1) + if negative: + result = '1' + ''.join(['10'[d=='1'] for d in result]) + return result + + +def bit_count(num): + """ + return number of set bits + + Counting bits set, Brian Kernighan's way* + + unsigned int v; // count the number of bits set in v + unsigned int c; // c accumulates the total bits set in v + for (c = 0; v; c++) + { v &= v - 1; } //clear the least significant bit set + + This method goes through as many iterations as there are set bits. So if we + have a 32-bit word with only the high bit set, then it will only go once + through the loop. + + * The C Programming Language 2nd Ed., Kernighan & Ritchie, 1988. + + This works because each subtraction "borrows" from the lowest 1-bit. For example: + + loop pass 1 loop pass 2 + ----------- ----------- + 101000 100000 + - 1 - 1 + = 100111 = 011111 + & 101000 & 100000 + = 100000 = 0 + + It is an excellent technique for Python, since the size of the integer need not + be determined beforehand. + """ + count = 0 + while(num): + num &= num - 1 + count += 1 + return(count) + +def bit_len(num): + length = 0 + while num: + length += 1 + num >>= 1 + return length + +def is_single_bit(num): + """ + True if only one bit set in num (should be an int) + """ + num &= num - 1 + return num == 0 + +def _make_class_unpicklable(obj): + """ + Make the given obj un-picklable. + + obj should be either a dictionary, on an Enum + """ + def _break_on_call_reduce(self, proto): + raise TypeError('%r cannot be pickled' % self) + if isinstance(obj, dict): + obj['__reduce_ex__'] = _break_on_call_reduce + obj['__module__'] = '' + else: + setattr(obj, '__reduce_ex__', _break_on_call_reduce) + setattr(obj, '__module__', '') + +def _check_auto_args(method): + """check if new generate method supports *args and **kwds""" + if isinstance(method, staticmethod): + method = method.__get__(type) + method = getattr(method, 'im_func', method) + args, varargs, keywords, defaults = getargspec(method) + return varargs is not None and keywords is not None + +def _get_attr_from_chain(cls, attr): + sentinel = object() + for basecls in cls.mro(): + obj = basecls.__dict__.get(attr, sentinel) + if obj is not sentinel: + return obj + +def _value(obj): + if isinstance(obj, (auto, constant)): + return obj.value + else: + return obj + +def enumsort(things): + """ + sorts things by value if all same type; otherwise by name + """ + if not things: + return things + sort_type = type(things[0]) + if not issubclass(sort_type, tuple): + # direct sort or type error + if not all((type(v) is sort_type) for v in things[1:]): + raise TypeError('cannot sort items of different types') + return sorted(things) + else: + # expecting list of (name, value) tuples + sort_type = type(things[0][1]) + try: + if all((type(v[1]) is sort_type) for v in things[1:]): + return sorted(things, key=lambda i: i[1]) + else: + raise TypeError('try name sort instead') + except TypeError: + return sorted(things, key=lambda i: i[0]) + +def export(collection, namespace=None): + """ + export([collection,] namespace) -> Export members to target namespace. + + If collection is not given, act as a decorator. + """ + if namespace is None: + namespace = collection + def export_decorator(collection): + return export(collection, namespace) + return export_decorator + elif issubclass(collection, NamedConstant): + for n, c in collection.__dict__.items(): + if isinstance(c, NamedConstant): + namespace[n] = c + elif issubclass(collection, Enum): + data = collection.__members__.items() + for n, m in data: + namespace[n] = m + else: + raise TypeError('%r is not a supported collection' % (collection,) ) + return collection + +class _Addendum(object): + def __init__(self, dict, doc, ns): + # dict is the dict to update with functions + # doc is the docstring to put in the dict + # ns is the namespace to remove the function names from + self.dict = dict + self.ns = ns + self.added = set() + def __call__(self, func): + if isinstance(func, (staticmethod, classmethod)): + name = func.__func__.__name__ + elif isinstance(func, (property, _bltin_property)): + name = (func.fget or func.fset or func.fdel).__name__ + else: + name = func.__name__ + self.dict[name] = func + self.added.add(name) + def resolve(self): + ns = self.ns + for name in self.added: + del ns[name] + return self.dict + +# Constant / NamedConstant + +class constant(object): + ''' + Simple constant descriptor for NamedConstant and Enum use. + ''' + def __init__(self, value, doc=None): + self.value = value + self.__doc__ = doc + + def __get__(self, *args): + return self.value + + def __repr__(self): + return '%s(%r)' % (self.__class__.__name__, self.value) + + def __and__(self, other): + return _and_(self.value, _value(other)) + + def __rand__(self, other): + return _and_(_value(other), self.value) + + def __invert__(self): + return _inv_(self.value) + + def __or__(self, other): + return _or_(self.value, _value(other)) + + def __ror__(self, other): + return _or_(_value(other), self.value) + + def __xor__(self, other): + return _xor_(self.value, _value(other)) + + def __rxor__(self, other): + return _xor_(_value(other), self.value) + + def __abs__(self): + return _abs_(self.value) + + def __add__(self, other): + return _add_(self.value, _value(other)) + + def __radd__(self, other): + return _add_(_value(other), self.value) + + def __neg__(self): + return _neg_(self.value) + + def __pos__(self): + return _pos_(self.value) + + if PY2: + def __div__(self, other): + return _div_(self.value, _value(other)) + + def __rdiv__(self, other): + return _div_(_value(other), (self.value)) + + def __floordiv__(self, other): + return _floordiv_(self.value, _value(other)) + + def __rfloordiv__(self, other): + return _floordiv_(_value(other), self.value) + + def __truediv__(self, other): + return _truediv_(self.value, _value(other)) + + def __rtruediv__(self, other): + return _truediv_(_value(other), self.value) + + def __lshift__(self, other): + return _lshift_(self.value, _value(other)) + + def __rlshift__(self, other): + return _lshift_(_value(other), self.value) + + def __rshift__(self, other): + return _rshift_(self.value, _value(other)) + + def __rrshift__(self, other): + return _rshift_(_value(other), self.value) + + def __mod__(self, other): + return _mod_(self.value, _value(other)) + + def __rmod__(self, other): + return _mod_(_value(other), self.value) + + def __mul__(self, other): + return _mul_(self.value, _value(other)) + + def __rmul__(self, other): + return _mul_(_value(other), self.value) + + def __pow__(self, other): + return _pow_(self.value, _value(other)) + + def __rpow__(self, other): + return _pow_(_value(other), self.value) + + def __sub__(self, other): + return _sub_(self.value, _value(other)) + + def __rsub__(self, other): + return _sub_(_value(other), self.value) + + def __set_name__(self, ownerclass, name): + self.name = name + self.clsname = ownerclass.__name__ + + +NamedConstant = None + +class _NamedConstantDict(dict): + """Track constant order and ensure names are not reused. + + NamedConstantMeta will use the names found in self._names as the + Constant names. + """ + def __init__(self): + super(_NamedConstantDict, self).__init__() + self._names = [] + + def __setitem__(self, key, value): + """Changes anything not dundered or not a constant descriptor. + + If an constant name is used twice, an error is raised; duplicate + values are not checked for. + + Single underscore (sunder) names are reserved. + """ + if _is_sunder(key): + raise ValueError( + '_sunder_ names, such as %r, are reserved for future NamedConstant use' + % (key, ) + ) + elif _is_dunder(key): + pass + elif key in self._names: + # overwriting an existing constant? + raise TypeError('attempt to reuse name: %r' % (key, )) + elif isinstance(value, constant) or not _is_descriptor(value): + if key in self: + # overwriting a descriptor? + raise TypeError('%s already defined as: %r' % (key, self[key])) + self._names.append(key) + super(_NamedConstantDict, self).__setitem__(key, value) + + +class NamedConstantMeta(type): + """ + Block attempts to reassign NamedConstant attributes. + """ + + @classmethod + def __prepare__(metacls, cls, bases, **kwds): + return _NamedConstantDict() + + def __new__(metacls, cls, bases, clsdict): + if type(clsdict) is dict: + original_dict = clsdict + clsdict = _NamedConstantDict() + for k, v in original_dict.items(): + clsdict[k] = v + newdict = {} + constants = {} + for name, obj in clsdict.items(): + if name in clsdict._names: + constants[name] = obj + continue + elif isinstance(obj, nonmember): + obj = obj.value + newdict[name] = obj + newcls = super(NamedConstantMeta, metacls).__new__(metacls, cls, bases, newdict) + newcls._named_constant_cache_ = {} + newcls._members_ = {} + for name, obj in constants.items(): + new_k = newcls.__new__(newcls, name, obj) + newcls._members_[name] = new_k + return newcls + + def __bool__(cls): + return True + + def __delattr__(cls, attr): + cur_obj = cls.__dict__.get(attr) + if NamedConstant is not None and isinstance(cur_obj, NamedConstant): + raise AttributeError('cannot delete constant <%s.%s>' % (cur_obj.__class__.__name__, cur_obj._name_)) + super(NamedConstantMeta, cls).__delattr__(attr) + + def __iter__(cls): + return (k for k in cls._members_.values()) + + def __reversed__(cls): + return (k for k in reversed(cls._members_.values())) + + def __len__(cls): + return len(cls._members_) + + __nonzero__ = __bool__ + + def __setattr__(cls, name, value): + """Block attempts to reassign NamedConstants. + """ + cur_obj = cls.__dict__.get(name) + if NamedConstant is not None and isinstance(cur_obj, NamedConstant): + raise AttributeError('cannot rebind constant <%s.%s>' % (cur_obj.__class__.__name__, cur_obj._name_)) + super(NamedConstantMeta, cls).__setattr__(name, value) + +constant_dict = _Addendum( + dict=NamedConstantMeta.__prepare__('NamedConstant', (object, )), + doc="NamedConstants protection.\n\n Derive from this class to lock NamedConstants.\n\n", + ns=globals(), + ) + +@constant_dict +def __new__(cls, name, value=None, doc=None): + if value is None: + # lookup, name is value + value = name + for name, obj in cls.__dict__.items(): + if isinstance(obj, cls) and obj._value_ == value: + return obj + else: + raise ValueError('%r does not exist in %r' % (value, cls.__name__)) + cur_obj = cls.__dict__.get(name) + if isinstance(cur_obj, NamedConstant): + raise AttributeError('cannot rebind constant <%s.%s>' % (cur_obj.__class__.__name__, cur_obj._name_)) + elif isinstance(value, constant): + doc = doc or value.__doc__ + value = value.value + metacls = cls.__class__ + if isinstance(value, NamedConstant): + # constants from other classes are reduced to their actual value + value = value._value_ + actual_type = type(value) + value_type = cls._named_constant_cache_.get(actual_type) + if value_type is None: + value_type = type(cls.__name__, (cls, type(value)), {}) + cls._named_constant_cache_[type(value)] = value_type + obj = actual_type.__new__(value_type, value) + obj._name_ = name + obj._value_ = value + obj.__doc__ = doc + cls._members_[name] = obj + metacls.__setattr__(cls, name, obj) + return obj + +@constant_dict +def __repr__(self): + return "<%s.%s: %r>" % ( + self.__class__.__name__, self._name_, self._value_) + +@constant_dict +def __reduce_ex__(self, proto): + return getattr, (self.__class__, self._name_) + +NamedConstant = NamedConstantMeta('NamedConstant', (object, ), constant_dict.resolve()) +Constant = NamedConstant +del constant_dict + +# NamedTuple + +class _NamedTupleDict(OrderedDict): + """Track field order and ensure field names are not reused. + + NamedTupleMeta will use the names found in self._field_names to translate + to indices. + """ + def __init__(self, *args, **kwds): + self._field_names = [] + super(_NamedTupleDict, self).__init__(*args, **kwds) + + def __setitem__(self, key, value): + """Records anything not dundered or not a descriptor. + + If a field name is used twice, an error is raised. + + Single underscore (sunder) names are reserved. + """ + if _is_sunder(key): + if key not in ('_size_', '_order_', '_fields_'): + raise ValueError( + '_sunder_ names, such as %r, are reserved for future NamedTuple use' + % (key, ) + ) + elif _is_dunder(key): + if key == '__order__': + key = '_order_' + elif key in self._field_names: + # overwriting a field? + raise TypeError('attempt to reuse field name: %r' % (key, )) + elif not _is_descriptor(value): + if key in self: + # field overwriting a descriptor? + raise TypeError('%s already defined as: %r' % (key, self[key])) + self._field_names.append(key) + super(_NamedTupleDict, self).__setitem__(key, value) + + +class _TupleAttributeAtIndex(object): + + def __init__(self, name, index, doc, default): + self.name = name + self.index = index + if doc is undefined: + doc = None + self.__doc__ = doc + self.default = default + + def __get__(self, instance, owner): + if instance is None: + return self + if len(instance) <= self.index: + raise AttributeError('%s instance has no value for %s' % (instance.__class__.__name__, self.name)) + return instance[self.index] + + def __repr__(self): + return '%s(%d)' % (self.__class__.__name__, self.index) + + +class undefined(object): + def __repr__(self): + return 'undefined' + def __bool__(self): + return False + __nonzero__ = __bool__ +undefined = undefined() + + +class TupleSize(NamedConstant): + fixed = constant('fixed', 'tuple length is static') + minimum = constant('minimum', 'tuple must be at least x long (x is calculated during creation') + variable = constant('variable', 'tuple length can be anything') + +class NamedTupleMeta(type): + """Metaclass for NamedTuple""" + + @classmethod + def __prepare__(metacls, cls, bases, size=undefined, **kwds): + return _NamedTupleDict() + + def __init__(cls, *args , **kwds): + super(NamedTupleMeta, cls).__init__(*args) + + def __new__(metacls, cls, bases, clsdict, size=undefined, **kwds): + if bases == (object, ): + bases = (tuple, object) + elif tuple not in bases: + if object in bases: + index = bases.index(object) + bases = bases[:index] + (tuple, ) + bases[index:] + else: + bases = bases + (tuple, ) + # include any fields from base classes + base_dict = _NamedTupleDict() + namedtuple_bases = [] + for base in bases: + if isinstance(base, NamedTupleMeta): + namedtuple_bases.append(base) + i = 0 + if namedtuple_bases: + for name, index, doc, default in metacls._convert_fields(*namedtuple_bases): + base_dict[name] = index, doc, default + i = max(i, index) + # construct properly ordered dict with normalized indexes + for k, v in clsdict.items(): + base_dict[k] = v + original_dict = base_dict + if size is not undefined and '_size_' in original_dict: + raise TypeError('_size_ cannot be set if "size" is passed in header') + add_order = isinstance(clsdict, _NamedTupleDict) + clsdict = _NamedTupleDict() + clsdict.setdefault('_size_', size or TupleSize.fixed) + unnumbered = OrderedDict() + numbered = OrderedDict() + _order_ = original_dict.pop('_order_', []) + if _order_ : + _order_ = _order_.replace(',',' ').split() + add_order = False + # and process this class + for k, v in original_dict.items(): + if k not in original_dict._field_names: + clsdict[k] = v + else: + # TODO:normalize v here + if isinstance(v, baseinteger): + # assume an offset + v = v, undefined, undefined + i = v[0] + 1 + target = numbered + elif isinstance(v, basestring): + # assume a docstring + if add_order: + v = i, v, undefined + i += 1 + target = numbered + else: + v = undefined, v, undefined + target = unnumbered + elif isinstance(v, tuple) and len(v) in (2, 3) and isinstance(v[0], baseinteger) and isinstance(v[1], (basestring, NoneType)): + # assume an offset, a docstring, and (maybe) a default + if len(v) == 2: + v = v + (undefined, ) + v = v + i = v[0] + 1 + target = numbered + elif isinstance(v, tuple) and len(v) in (1, 2) and isinstance(v[0], (basestring, NoneType)): + # assume a docstring, and (maybe) a default + if len(v) == 1: + v = v + (undefined, ) + if add_order: + v = (i, ) + v + i += 1 + target = numbered + else: + v = (undefined, ) + v + target = unnumbered + else: + # refuse to guess further + raise ValueError('not sure what to do with %s=%r (should be OFFSET [, DOC [, DEFAULT]])' % (k, v)) + target[k] = v + # all index values have been normalized + # deal with _order_ (or lack thereof) + fields = [] + aliases = [] + seen = set() + max_len = 0 + if not _order_: + if unnumbered: + raise ValueError("_order_ not specified and OFFSETs not declared for %r" % (unnumbered.keys(), )) + for name, (index, doc, default) in sorted(numbered.items(), key=lambda nv: (nv[1][0], nv[0])): + if index in seen: + aliases.append(name) + else: + fields.append(name) + seen.add(index) + max_len = max(max_len, index + 1) + offsets = numbered + else: + # check if any unnumbered not in _order_ + missing = set(unnumbered) - set(_order_) + if missing: + raise ValueError("unable to order fields: %s (use _order_ or specify OFFSET" % missing) + offsets = OrderedDict() + # if any unnumbered, number them from their position in _order_ + i = 0 + for k in _order_: + try: + index, doc, default = unnumbered.pop(k, None) or numbered.pop(k) + except IndexError: + raise ValueError('%s (from _order_) not found in %s' % (k, cls)) + if index is not undefined: + i = index + if i in seen: + aliases.append(k) + else: + fields.append(k) + seen.add(i) + offsets[k] = i, doc, default + i += 1 + max_len = max(max_len, i) + # now handle anything in numbered + for k, (index, doc, default) in sorted(numbered.items(), key=lambda nv: (nv[1][0], nv[0])): + if index in seen: + aliases.append(k) + else: + fields.append(k) + seen.add(index) + offsets[k] = index, doc, default + max_len = max(max_len, index+1) + + # at this point fields and aliases should be ordered lists, offsets should be an + # OrdededDict with each value an int, str or None or undefined, default or None or undefined + assert len(fields) + len(aliases) == len(offsets), "number of fields + aliases != number of offsets" + assert set(fields) & set(offsets) == set(fields), "some fields are not in offsets: %s" % set(fields) & set(offsets) + assert set(aliases) & set(offsets) == set(aliases), "some aliases are not in offsets: %s" % set(aliases) & set(offsets) + for name, (index, doc, default) in offsets.items(): + assert isinstance(index, baseinteger), "index for %s is not an int (%s:%r)" % (name, type(index), index) + assert isinstance(doc, (basestring, NoneType)) or doc is undefined, "doc is not a str, None, nor undefined (%s:%r)" % (name, type(doc), doc) + + # create descriptors for fields + for name, (index, doc, default) in offsets.items(): + clsdict[name] = _TupleAttributeAtIndex(name, index, doc, default) + clsdict['__slots__'] = () + + # create our new NamedTuple type + namedtuple_class = super(NamedTupleMeta, metacls).__new__(metacls, cls, bases, clsdict) + namedtuple_class._fields_ = fields + namedtuple_class._aliases_ = aliases + namedtuple_class._defined_len_ = max_len + return namedtuple_class + + @staticmethod + def _convert_fields(*namedtuples): + "create list of index, doc, default triplets for cls in namedtuples" + all_fields = [] + for cls in namedtuples: + base = len(all_fields) + for field in cls._fields_: + desc = getattr(cls, field) + all_fields.append((field, base+desc.index, desc.__doc__, desc.default)) + return all_fields + + def __add__(cls, other): + "A new NamedTuple is created by concatenating the _fields_ and adjusting the descriptors" + if not isinstance(other, NamedTupleMeta): + return NotImplemented + return NamedTupleMeta('%s%s' % (cls.__name__, other.__name__), (cls, other), {}) + + def __call__(cls, *args, **kwds): + """Creates a new NamedTuple class or an instance of a NamedTuple subclass. + + NamedTuple should have args of (class_name, names, module) + + `names` can be: + + * A string containing member names, separated either with spaces or + commas. Values are auto-numbered from 1. + * An iterable of member names. Values are auto-numbered from 1. + * An iterable of (member name, value) pairs. + * A mapping of member name -> value. + + `module`, if set, will be stored in the new class' __module__ attribute; + + Note: if `module` is not set this routine will attempt to discover the + calling module by walking the frame stack; if this is unsuccessful + the resulting class will not be pickleable. + + subclass should have whatever arguments and/or keywords will be used to create an + instance of the subclass + """ + if cls is NamedTuple: + original_args = args + original_kwds = kwds.copy() + # create a new subclass + try: + if 'class_name' in kwds: + class_name = kwds.pop('class_name') + else: + class_name, args = args[0], args[1:] + if 'names' in kwds: + names = kwds.pop('names') + else: + names, args = args[0], args[1:] + if 'module' in kwds: + module = kwds.pop('module') + elif args: + module, args = args[0], args[1:] + else: + module = None + if 'type' in kwds: + type = kwds.pop('type') + elif args: + type, args = args[0], args[1:] + else: + type = None + + except IndexError: + raise TypeError('too few arguments to NamedTuple: %s, %s' % (original_args, original_kwds)) + if args or kwds: + raise TypeError('too many arguments to NamedTuple: %s, %s' % (original_args, original_kwds)) + if PY2: + # if class_name is unicode, attempt a conversion to ASCII + if isinstance(class_name, unicode): + try: + class_name = class_name.encode('ascii') + except UnicodeEncodeError: + raise TypeError('%r is not representable in ASCII' % (class_name, )) + # quick exit if names is a NamedTuple + if isinstance(names, NamedTupleMeta): + names.__name__ = class_name + if type is not None and type not in names.__bases__: + names.__bases__ = (type, ) + names.__bases__ + return names + + metacls = cls.__class__ + bases = (cls, ) + clsdict = metacls.__prepare__(class_name, bases) + + # special processing needed for names? + if isinstance(names, basestring): + names = names.replace(',', ' ').split() + if isinstance(names, (tuple, list)) and isinstance(names[0], basestring): + names = [(e, i) for (i, e) in enumerate(names)] + # Here, names is either an iterable of (name, index) or (name, index, doc, default) or a mapping. + item = None # in case names is empty + for item in names: + if isinstance(item, basestring): + # mapping + field_name, field_index = item, names[item] + else: + # non-mapping + if len(item) == 2: + field_name, field_index = item + else: + field_name, field_index = item[0], item[1:] + clsdict[field_name] = field_index + if type is not None: + if not isinstance(type, tuple): + type = (type, ) + bases = type + bases + namedtuple_class = metacls.__new__(metacls, class_name, bases, clsdict) + + # TODO: replace the frame hack if a blessed way to know the calling + # module is ever developed + if module is None: + try: + module = _sys._getframe(1).f_globals['__name__'] + except (AttributeError, ValueError, KeyError): + pass + if module is None: + _make_class_unpicklable(namedtuple_class) + else: + namedtuple_class.__module__ = module + + return namedtuple_class + else: + # instantiate a subclass + namedtuple_instance = cls.__new__(cls, *args, **kwds) + if isinstance(namedtuple_instance, cls): + namedtuple_instance.__init__(*args, **kwds) + return namedtuple_instance + + @_bltin_property + def __fields__(cls): + return list(cls._fields_) + # collections.namedtuple compatibility + _fields = __fields__ + + @_bltin_property + def __aliases__(cls): + return list(cls._aliases_) + + def __repr__(cls): + return "" % (cls.__name__, ) + +namedtuple_dict = _Addendum( + dict=NamedTupleMeta.__prepare__('NamedTuple', (object, )), + doc="NamedTuple base class.\n\n Derive from this class to define new NamedTuples.\n\n", + ns=globals(), + ) + +@namedtuple_dict +def __new__(cls, *args, **kwds): + if cls._size_ is TupleSize.fixed and len(args) > cls._defined_len_: + raise TypeError('%d fields expected, %d received' % (cls._defined_len_, len(args))) + unknown = set(kwds) - set(cls._fields_) - set(cls._aliases_) + if unknown: + raise TypeError('unknown fields: %r' % (unknown, )) + final_args = list(args) + [undefined] * (len(cls.__fields__) - len(args)) + for field, value in kwds.items(): + index = getattr(cls, field).index + if final_args[index] != undefined: + raise TypeError('field %s specified more than once' % field) + final_args[index] = value + missing = [] + for index, value in enumerate(final_args): + if value is undefined: + # look for default values + name = cls.__fields__[index] + default = getattr(cls, name).default + if default is undefined: + missing.append(name) + else: + final_args[index] = default + if missing: + if cls._size_ in (TupleSize.fixed, TupleSize.minimum): + raise TypeError('values not provided for field(s): %s' % ', '.join(missing)) + while final_args and final_args[-1] is undefined: + final_args.pop() + missing.pop() + if cls._size_ is not TupleSize.variable or undefined in final_args: + raise TypeError('values not provided for field(s): %s' % ', '.join(missing)) + return tuple.__new__(cls, tuple(final_args)) + +@namedtuple_dict +def __reduce_ex__(self, proto): + return self.__class__, tuple(getattr(self, f) for f in self._fields_) + +@namedtuple_dict +def __repr__(self): + if len(self) == len(self._fields_): + return "%s(%s)" % ( + self.__class__.__name__, ', '.join(['%s=%r' % (f, o) for f, o in zip(self._fields_, self)]) + ) + else: + return '%s(%s)' % (self.__class__.__name__, ', '.join([repr(o) for o in self])) + +@namedtuple_dict +def __str__(self): + return "%s(%s)" % ( + self.__class__.__name__, ', '.join(['%r' % (getattr(self, f), ) for f in self._fields_]) + ) + +@namedtuple_dict +@_bltin_property +def _fields_(self): + return list(self.__class__._fields_) + + # compatibility methods with stdlib namedtuple +@namedtuple_dict +@_bltin_property +def __aliases__(self): + return list(self.__class__._aliases_) + +@namedtuple_dict +@_bltin_property +def _fields(self): + return list(self.__class__._fields_) + +@namedtuple_dict +@classmethod +def _make(cls, iterable, new=None, len=None): + return cls.__new__(cls, *iterable) + +@namedtuple_dict +def _asdict(self): + return OrderedDict(zip(self._fields_, self)) + +@namedtuple_dict +def _replace(self, **kwds): + current = self._asdict() + current.update(kwds) + return self.__class__(**current) + +NamedTuple = NamedTupleMeta('NamedTuple', (object, ), namedtuple_dict.resolve()) +del namedtuple_dict + + +# Enum + + # _init_ and value and AddValue + # ----------------------------- + # by default, when defining a member everything after the = is "the value", everything is + # passed to __new__, everything is passed to __init__ + # + # if _init_ is present then + # if `value` is not in _init_, everything is "the value", defaults apply + # if `value` is in _init_, only the first thing after the = is the value, and the rest will + # be passed to __init__ + # if fewer values are present for member assignment than _init_ calls for, _generate_next_value_ + # will be called in an attempt to generate them + # + # if AddValue is present then + # _generate_next_value_ is always called, and any generated values are prepended to provided + # values (custom _gnv_s can change that) + # default _init_ rules apply + + + # Constants used in Enum + +@export(globals()) +class EnumConstants(NamedConstant): + AddValue = constant('addvalue', 'prepends value(s) from _generate_next_value_ to each member') + MagicValue = constant('magicvalue', 'calls _generate_next_value_ when no arguments are given') + MultiValue = constant('multivalue', 'each member can have several values') + NoAlias = constant('noalias', 'duplicate valued members are distinct, not aliased') + Unique = constant('unique', 'duplicate valued members are not allowed') + def __repr__(self): + return self._name_ + + + # Dummy value for Enum as EnumType explicity checks for it, but of course until + # EnumType finishes running the first time the Enum class doesn't exist. This + # is also why there are checks in EnumType like `if Enum is not None`. + # + # Ditto for Flag. + +Enum = ReprEnum = IntEnum = StrEnum = Flag = IntFlag = EJECT = KEEP = None + +class enum(object): + """ + Helper class to track args, kwds. + """ + def __init__(self, *args, **kwds): + self._args = args + self._kwds = dict(kwds.items()) + self._hash = hash(args) + self.name = None + + @_bltin_property + def args(self): + return self._args + + @_bltin_property + def kwds(self): + return self._kwds.copy() + + def __hash__(self): + return self._hash + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return self.args == other.args and self.kwds == other.kwds + + def __ne__(self, other): + if not isinstance(other, self.__class__): + return NotImplemented + return self.args != other.args or self.kwds != other.kwds + + def __repr__(self): + final = [] + args = ', '.join(['%r' % (a, ) for a in self.args]) + if args: + final.append(args) + kwds = ', '.join([('%s=%r') % (k, v) for k, v in enumsort(list(self.kwds.items()))]) + if kwds: + final.append(kwds) + return '%s(%s)' % (self.__class__.__name__, ', '.join(final)) + +_auto_null = SentinelType('no_value', (object, ), {}) +class auto(enum): + """ + Instances are replaced with an appropriate value in Enum class suites. + """ + enum_member = _auto_null + _value = _auto_null + _operations = [] + + def __and__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_and_, (self, other))) + return new_auto + + def __rand__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_and_, (other, self))) + return new_auto + + def __invert__(self): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_inv_, (self,))) + return new_auto + + def __or__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_or_, (self, other))) + return new_auto + + def __ror__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_or_, (other, self))) + return new_auto + + def __xor__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_xor_, (self, other))) + return new_auto + + def __rxor__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_xor_, (other, self))) + return new_auto + + def __abs__(self): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_abs_, (self, ))) + return new_auto + + def __add__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_add_, (self, other))) + return new_auto + + def __radd__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_add_, (other, self))) + return new_auto + + def __neg__(self): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_neg_, (self, ))) + return new_auto + + def __pos__(self): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_pos_, (self, ))) + return new_auto + + if PY2: + def __div__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_div_, (self, other))) + return new_auto + + def __rdiv__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_div_, (other, self))) + return new_auto + + def __floordiv__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_floordiv_, (self, other))) + return new_auto + + def __rfloordiv__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_floordiv_, (other, self))) + return new_auto + + def __truediv__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_truediv_, (self, other))) + return new_auto + + def __rtruediv__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_truediv_, (other, self))) + return new_auto + + def __lshift__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_lshift_, (self, other))) + return new_auto + + def __rlshift__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_lshift_, (other, self))) + return new_auto + + def __rshift__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_rshift_, (self, other))) + return new_auto + + def __rrshift__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_rshift_, (other, self))) + return new_auto + + def __mod__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_mod_, (self, other))) + return new_auto + + def __rmod__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_mod_, (other, self))) + return new_auto + + def __mul__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_mul_, (self, other))) + return new_auto + + def __rmul__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_mul_, (other, self))) + return new_auto + + def __pow__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_pow_, (self, other))) + return new_auto + + def __rpow__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_pow_, (other, self))) + return new_auto + + def __sub__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_sub_, (self, other))) + return new_auto + + def __rsub__(self, other): + new_auto = self.__class__() + new_auto._operations = self._operations[:] + new_auto._operations.append((_sub_, (other, self))) + return new_auto + + def __repr__(self): + if self._operations: + return 'auto(...)' + else: + return 'auto(%r, *%r, **%r)' % (self._value, self._args, self._kwds) + + @_bltin_property + def value(self): + if self._value is not _auto_null and self._operations: + raise TypeError('auto() object out of sync') + elif self._value is _auto_null and not self._operations: + return self._value + elif self._value is not _auto_null: + return self._value + else: + return self._resolve() + + @value.setter + def value(self, value): + if self._operations: + value = self._resolve(value) + self._value = value + + def _resolve(self, base_value=None): + cls = self.__class__ + for op, params in self._operations: + values = [] + for param in params: + if isinstance(param, cls): + if param.value is _auto_null: + if base_value is None: + return _auto_null + else: + values.append(base_value) + else: + values.append(param.value) + else: + values.append(param) + value = op(*values) + self._operations[:] = [] + self._value = value + return value + + +class _EnumArgSpec(NamedTuple): + args = 0, 'all args except *args and **kwds' + varargs = 1, 'the name of the *args variable' + keywords = 2, 'the name of the **kwds variable' + defaults = 3, 'any default values' + required = 4, 'number of required values (no default available)' + + def __new__(cls, _new_func): + argspec = getargspec(_new_func) + args, varargs, keywords, defaults = argspec + if defaults: + reqs = args[1:-len(defaults)] + else: + reqs = args[1:] + return tuple.__new__(_EnumArgSpec, (args, varargs, keywords, defaults, reqs)) + + +class _proto_member: + """ + intermediate step for enum members between class execution and final creation + """ + + def __init__(self, value): + self.value = value + + def __set_name__(self, enum_class, member_name): + """ + convert each quasi-member into an instance of the new enum class + """ + # first step: remove ourself from enum_class + delattr(enum_class, member_name) + # second step: create member based on enum_class + value = self.value + kwds = {} + args = () + init_args = () + extra_mv_args = () + multivalue = None + if isinstance(value, tuple) and value and isinstance(value[0], auto): + multivalue = value + value = value[0] + if isinstance(value, auto) and value.value is _auto_null: + args = value.args + kwds = value.kwds + elif isinstance(value, auto): + kwds = value.kwds + args = (value.value, ) + value.args + value = value.value + elif isinstance(value, enum): + args = value.args + kwds = value.kwds + elif isinstance(value, Member): + value = value.value + args = (value, ) + elif not isinstance(value, tuple): + args = (value, ) + else: + args = value + if multivalue is not None: + value = (value, ) + multivalue[1:] + kwds = {} + args = value + del multivalue + # possibilities + # + # - no init, multivalue -> __new__[0], __init__(*[:]), extra=[1:] + # - init w/o value, multivalue -> __new__[0], __init__(*[:]), extra=[1:] + # + # - init w/value, multivalue -> __new__[0], __init__(*[1:]), extra=[1:] + # + # - init w/value, no multivalue -> __new__[0], __init__(*[1:]), extra=[] + # + # - init w/o value, no multivalue -> __new__[:], __init__(*[:]), extra=[] + # - no init, no multivalue -> __new__[:], __init__(*[:]), extra=[] + if enum_class._multivalue_ or 'value' in enum_class._creating_init_: + if enum_class._multivalue_: + # when multivalue is True, creating_init can be anything + mv_arg = args[0] + extra_mv_args = args[1:] + if 'value' in enum_class._creating_init_: + init_args = args[1:] + else: + init_args = args + args = args[0:1] + value = args[0] + else: + # 'value' is definitely in creating_init + if enum_class._auto_init_ and enum_class._new_args_: + # we have a custom __new__ and an auto __init__ + # divvy up according to number of params in each + init_args = args[-len(enum_class._creating_init_)+1:] + if not enum_class._auto_args_: + args = args[:len(enum_class._new_args_.args)] + value = args[0] + elif enum_class._auto_init_: + # don't pass in value + init_args = args[1:] + args = args[0:1] + value = args[0] + elif enum_class._new_args_: + # do not modify args + value = args[0] + else: + # keep all args for user-defined __init__ + # keep value as-is + init_args = args + else: + # either no creating_init, or it doesn't have 'value' + init_args = args + if enum_class._member_type_ is tuple: # special case for tuple enums + args = (args, ) # wrap it one more time + if not enum_class._use_args_: + enum_member = enum_class._new_member_(enum_class) + if not hasattr(enum_member, '_value_'): + enum_member._value_ = value + else: + enum_member = enum_class._new_member_(enum_class, *args, **kwds) + if not hasattr(enum_member, '_value_'): + if enum_class._member_type_ is object: + enum_member._value_ = value + else: + try: + enum_member._value_ = enum_class._member_type_(*args, **kwds) + except Exception: + te = TypeError('_value_ not set in __new__, unable to create it') + te.__cause__ = None + raise te + value = enum_member._value_ + enum_member._name_ = member_name + enum_member.__objclass__ = enum_class + enum_member.__init__(*init_args, **kwds) + enum_member._sort_order_ = len(enum_class._member_names_) + # If another member with the same value was already defined, the + # new member becomes an alias to the existing one. + if enum_class._noalias_: + # unless NoAlias was specified + enum_class._member_names_.append(member_name) + else: + nonunique = defaultdict(list) + try: + try: + # try to do a fast lookup to avoid the quadratic loop + enum_member = enum_class._value2member_map_[value] + if enum_class._unique_: + nonunique[enum_member.name].append(member_name) + except TypeError: + # unhashable members are stored elsewhere + for unhashable_value, canonical_member in enum_class._value2member_seq_: + name = canonical_member.name + if unhashable_value == enum_member._value_: + if enum_class._unique_: + nonunique[name].append(member_name) + enum_member = canonical_member + break + else: + raise KeyError + except KeyError: + # this could still be an alias if the value is multi-bit and the + # class is a flag class + if ( + Flag is None + or not issubclass(enum_class, Flag) + ): + # no other instances found, record this member in _member_names_ + enum_class._member_names_.append(member_name) + elif ( + Flag is not None + and issubclass(enum_class, Flag) + and _is_single_bit(value) + ): + # no other instances found, record this member in _member_names_ + enum_class._member_names_.append(member_name) + if nonunique: + # duplicates not allowed if Unique specified + message = [] + for name, aliases in nonunique.items(): + bad_aliases = ','.join(aliases) + message.append('%s --> %s [%r]' % (name, bad_aliases, enum_class[name].value)) + raise ValueError( + '%s: duplicate names found: %s' % + (enum_class.__name__, '; '.join(message)) + ) + # if self.value is an `auto()`, replace the value attribute with the new enum member + if isinstance(self.value, auto): + self.value.enum_member = enum_member + # get redirect in place before adding to _member_map_ + # but check for other instances in parent classes first + need_override = False + descriptor = None + descriptor_property = None + for base in enum_class.__mro__[1:]: + descriptor = base.__dict__.get(member_name) + if descriptor is not None: + if isinstance(descriptor, (property, DynamicClassAttribute)): + break + else: + need_override = True + if isinstance(descriptor, _bltin_property) and descriptor_property is None: + descriptor_property = descriptor + # keep looking for an enum.property + descriptor = descriptor or descriptor_property + if descriptor and not need_override: + # previous enum.property found, no further action needed + pass + else: + redirect = property() + redirect.__set_name__(enum_class, member_name) + if descriptor and need_override: + # previous enum.property found, but some other inherited + # attribute is in the way; copy fget, fset, fdel to this one + redirect.fget = descriptor.fget + redirect.fset = descriptor.fset + redirect.fdel = descriptor.fdel + setattr(enum_class, member_name, redirect) + # now add to _member_map_ (even aliases) + enum_class._member_map_[member_name] = enum_member + # + # process (possible) MultiValues + values = (value, ) + extra_mv_args + if enum_class._multivalue_ and mv_arg not in values: + values += (mv_arg, ) + enum_member._values_ = values + for value in values: + # first check if value has already been used + if enum_class._multivalue_ and ( + value in enum_class._value2member_map_ + or any(v == value for (v, m) in enum_class._value2member_seq_) + ): + raise ValueError('%r has already been used' % (value, )) + try: + # This may fail if value is not hashable. We can't add the value + # to the map, and by-value lookups for this value will be + # linear. + if enum_class._noalias_: + raise TypeError('cannot use dict to store value') + enum_class._value2member_map_[value] = enum_member + except TypeError: + enum_class._value2member_seq_ += ((value, enum_member), ) + + +class _EnumDict(dict): + """Track enum member order and ensure member names are not reused. + + EnumType will use the names found in self._member_names as the + enumeration member names. + """ + def __init__(self, cls_name, settings, start, constructor_init, constructor_start, constructor_boundary): + super(_EnumDict, self).__init__() + self._cls_name = cls_name + self._constructor_init = constructor_init + self._constructor_start = constructor_start + self._constructor_boundary = constructor_boundary + self._generate_next_value = None + self._member_names = [] + self._member_names_set = set() + self._settings = settings + self._addvalue = addvalue = AddValue in settings + self._magicvalue = MagicValue in settings + self._multivalue = MultiValue in settings + if self._addvalue and self._magicvalue: + raise TypeError('%r: AddValue and MagicValue are mutually exclusive' % cls_name) + if self._multivalue and self._magicvalue: + raise TypeError('%r: MultiValue and MagicValue are mutually exclusive' % cls_name) + self._start = start + self._addvalue_value = start + self._new_args = () + self._auto_args = False + # when the magic turns off + self._locked = MagicValue not in settings + # if init fields are specified + self._init = [] + # list of temporary names + self._ignore = [] + if self._magicvalue: + self._ignore = ['property', 'staticmethod', 'classmethod'] + self._ignore_init_done = False + # if _sunder_ values can be changed via the class body + self._allow_init = True + self._last_values = [] + + def __getitem__(self, key): + if key == self._cls_name and self._cls_name not in self: + return enum + elif ( + self._locked + or key in self + or key in self._ignore + or _is_sunder(key) + or _is_dunder(key) + ): + return super(_EnumDict, self).__getitem__(key) + elif self._magicvalue: + value = self._generate_next_value(key, self._start, len(self._member_names), self._last_values[:]) + self.__setitem__(key, value) + return value + else: + raise Exception('Magic is not set -- why am I here?') + + def __setitem__(self, key, value): + """Changes anything not sundured, dundered, nor a descriptor. + + If an enum member name is used twice, an error is raised; duplicate + values are not checked for. + + Single underscore (sunder) names are reserved. + """ + # Flag classes that have MagicValue and __new__ will get a generated _gnv_ + if _is_internal_class(self._cls_name, value): + pass + elif _is_private_name(self._cls_name, key): + pass + elif _is_sunder(key): + if key not in ( + '_init_', '_settings_', '_order_', '_ignore_', '_start_', + '_create_pseudo_member_', '_create_pseudo_member_values_', + '_generate_next_value_', '_boundary_', '_numeric_repr_', + '_missing_', '_missing_value_', '_missing_name_', + '_iter_member_', '_iter_member_by_value_', '_iter_member_by_def_', + ): + raise ValueError('%r: _sunder_ names, such as %r, are reserved for future Enum use' + % (self._cls_name, key) + ) + elif not self._allow_init and key not in ( + 'create_pseudo_member_', '_missing_', '_missing_value_', '_missing_name_', + ): + # sunder is used during creation, must be specified first + raise ValueError('%r: cannot set %r after init phase' % (self._cls_name, key)) + elif key == '_ignore_': + if self._ignore_init_done: + raise TypeError('%r: ignore can only be specified once' % self._cls_name) + if isinstance(value, basestring): + value = value.split() + else: + value = list(value) + self._ignore = value + already = set(value) & self._member_names_set + if already: + raise ValueError('%r: _ignore_ cannot specify already set names %s' % ( + self._cls_name, + ', '.join(repr(a) for a in already) + )) + self._ignore_init_done = True + elif key == '_boundary_': + if self._constructor_boundary: + raise TypeError('%r: boundary specified in constructor and class body' % self._cls_name) + elif key == '_start_': + if self._constructor_start: + raise TypeError('%r: start specified in constructor and class body' % self._cls_name) + self._start = value + elif key == '_settings_': + if not isinstance(value, (set, tuple)): + value = (value, ) + if not isinstance(value, set): + value = set(value) + self._settings |= value + if NoAlias in value and Unique in value: + raise TypeError('%r: NoAlias and Unique are mutually exclusive' % self._cls_name) + elif MultiValue in value and NoAlias in value: + raise TypeError('cannot specify both MultiValue and NoAlias' % self._cls_name) + allowed_settings = dict.fromkeys(['addvalue', 'magicvalue', 'noalias', 'unique', 'multivalue']) + for arg in self._settings: + if arg not in allowed_settings: + raise TypeError('%r: unknown qualifier %r (from %r)' % (self._cls_name, arg, value)) + allowed_settings[arg] = True + self._multivalue = allowed_settings['multivalue'] + self._addvalue = allowed_settings['addvalue'] + self._magicvalue = allowed_settings['magicvalue'] + self._locked = not self._magicvalue + if self._magicvalue and not self._ignore_init_done: + self._ignore = ['property', 'classmethod', 'staticmethod'] + if self._addvalue and self._init and 'value' not in self._init: + self._init.insert(0, 'value') + value = tuple(self._settings) + elif key == '_init_': + if self._constructor_init: + raise TypeError('%r: init specified in constructor and in class body' % self._cls_name) + _init_ = value + if isinstance(_init_, basestring): + _init_ = _init_.replace(',',' ').split() + if self._addvalue and 'value' not in self._init: + self._init.insert(0, 'value') + if self._magicvalue: + raise TypeError("%r: _init_ and MagicValue are mutually exclusive" % self._cls_name) + self._init = _init_ + value = _init_ + elif key == '_generate_next_value_': + gnv = value + if value is not None: + if isinstance(value, staticmethod): + gnv = value.__func__ + elif isinstance(value, classmethod): + raise TypeError('%r: _generate_next_value must be a staticmethod, not a classmethod' % self._cls_name) + else: + gnv = value + value = staticmethod(value) + self._auto_args = _check_auto_args(value) + setattr(self, '_generate_next_value', gnv) + elif _is_dunder(key): + if key == '__order__': + key = '_order_' + if not self._allow_init: + # _order_ is used during creation, must be specified first + raise ValueError('%r: cannot set %r after init phase' % (self._cls_name, key)) + elif key == '__new__': # and self._new_to_init: + if isinstance(value, staticmethod): + value = value.__func__ + self._new_args = _EnumArgSpec(value) + elif key == '__init_subclass__': + if not isinstance(value, classmethod): + value = classmethod(value) + if _is_descriptor(value): + self._locked = True + elif key in self._member_names_set: + # descriptor overwriting an enum? + raise TypeError('%r: attempt to reuse name: %r' % (self._cls_name, key)) + elif key in self._ignore: + pass + elif not _is_descriptor(value): + self._allow_init = False + if key in self: + # enum overwriting a descriptor? + raise TypeError('%r: %s already defined as %r' % (self._cls_name, key, self[key])) + if type(value) is enum: + value.name = key + if self._addvalue: + raise TypeError('%r: enum() and AddValue are incompatible' % self._cls_name) + elif self._addvalue and not self._multivalue: + # generate a value + value = self._gnv(key, value) + elif self._multivalue: + # make sure it's a tuple + if not isinstance(value, tuple): + value = (value, ) + if isinstance(value[0], auto): + value = (self._convert_auto(key, value[0]), ) + value[1:] + if self._addvalue: + value = self._gnv(key, value) + elif isinstance(value, auto): + value = self._convert_auto(key, value) + elif isinstance(value, tuple) and value and isinstance(value[0], auto): + value = (self._convert_auto(key, value[0]), ) + value[1:] + elif not isinstance(value, auto): + # call generate maybe if + # - init is specified; or + # - __new__ is specified; + # and either of them call for more values than are present + new_args = () or self._new_args and self._new_args.required + target_len = len(self._init or new_args) + if isinstance(value, tuple): + source_len = len(value) + else: + source_len = 1 + multi_args = len(self._init) > 1 or new_args + if source_len < target_len : + value = self._gnv(key, value) + else: + pass + if self._init: + if isinstance(value, auto): + test_value = value.args + elif not isinstance(value, tuple): + test_value = (value, ) + else: + test_value = value + if len(self._init) != len(test_value): + raise TypeError( + '%s.%s: number of fields provided do not match init [%r != %r]' + % (self._cls_name, key, self._init, test_value) + ) + self._member_names.append(key) + self._member_names_set.add(key) + else: + # not a new member, turn off the autoassign magic + self._locked = True + self._allow_init = False + if not (_is_sunder(key) or _is_dunder(key) or _is_private_name(self._cls_name, key) or _is_descriptor(value)): + if isinstance(value, auto): + self._last_values.append(value.value) + elif isinstance(value, tuple) and value and isinstance(value[0], auto): + self._last_values.append(value[0].value) + elif isinstance(value, tuple): + if value: + self._last_values.append(value[0]) + else: + self._last_values.append(value) + super(_EnumDict, self).__setitem__(key, value) + + def _convert_auto(self, key, value): + # if auto.args or auto.kwds, compare to _init_ and __new__ -- if lacking, call gnv + # if not auto.args|kwds but auto.value is _auto_null -- call gnv + if value.args or value.kwds or value.value is _auto_null: + if value.args or value.kwds: + values = value.args + else: + values = () + new_args = () or self._new_args and self._new_args.required + target_len = len(self._init or new_args) or 1 + if isinstance(values, tuple): + source_len = len(values) + else: + source_len = 1 + multi_args = len(self._init) > 1 or new_args + if source_len < target_len : + values = self._gnv(key, values) + if value.args: + value._args = values + else: + value.value = values + return value + + def _gnv(self, key, value): + # generate a value + if self._auto_args: + if not isinstance(value, tuple): + value = (value, ) + value = self._generate_next_value(key, self._start, len(self._member_names), self._last_values[:], *value) + else: + value = self._generate_next_value(key, self._start, len(self._member_names), self._last_values[:]) + if isinstance(value, tuple) and len(value) == 1: + value = value[0] + return value + + +no_arg = SentinelType('no_arg', (object, ), {}) +class EnumType(type): + """Metaclass for Enum""" + + @classmethod + def __prepare__(metacls, cls, bases, init=None, start=None, settings=(), boundary=None, **kwds): + metacls._check_for_existing_members_(cls, bases) + if Flag is None and cls == 'Flag': + initial_flag = True + else: + initial_flag = False + # settings are a combination of current and all past settings + constructor_init = init is not None + constructor_start = start is not None + constructor_boundary = boundary is not None + if not isinstance(settings, tuple): + settings = settings, + settings = set(settings) + generate = None + order = None + # inherit previous flags + member_type, first_enum = metacls._get_mixins_(cls, bases) + if first_enum is not None: + generate = getattr(first_enum, '_generate_next_value_', None) + generate = getattr(generate, 'im_func', generate) + settings |= metacls._get_settings_(bases) + init = init or first_enum._auto_init_[:] + order = first_enum._order_function_ + if start is None: + start = first_enum._start_ + else: + # first time through -- creating Enum itself + start = 1 + # check for custom settings + if AddValue in settings and init and 'value' not in init: + if isinstance(init, list): + init.insert(0, 'value') + else: + init = 'value ' + init + if NoAlias in settings and Unique in settings: + raise TypeError('%r: NoAlias and Unique are mutually exclusive' % cls) + if MultiValue in settings and NoAlias in settings: + raise TypeError('%r: MultiValue and NoAlias are mutually exclusive' % cls) + allowed_settings = dict.fromkeys(['addvalue', 'magicvalue', 'noalias', 'unique', 'multivalue']) + for arg in settings: + if arg not in allowed_settings: + raise TypeError('%r: unknown qualifier %r' % (cls, arg)) + enum_dict = _EnumDict(cls_name=cls, settings=settings, start=start, constructor_init=constructor_init, constructor_start=constructor_start, constructor_boundary=constructor_boundary) + enum_dict._member_type = member_type + enum_dict._base_type = ('enum', 'flag')[ + Flag is None and cls == 'Flag' + or + Flag is not None and any(issubclass(b, Flag) for b in bases) + ] + if Flag is not None and any(b is Flag for b in bases) and member_type not in (baseinteger + (object, )): + if Flag in bases: + # when a non-int data type is mixed in with Flag, we end up + # needing two values for two `__new__`s: + # - the integer value for the Flag itself; and + # - the mix-in value for the mix-in + # + # we provide a default `_generate_next_value_` to supply the int + # argument, and a default `__new__` to keep the two straight + def _generate_next_value_(name, start, count, values, *args, **kwds): + return (2 ** count, ) + args + enum_dict['_generate_next_value_'] = staticmethod(_generate_next_value_) + def __new__(cls, flag_value, type_value): + obj = member_type.__new__(cls, type_value) + obj._value_ = flag_value + return obj + enum_dict['__new__'] = __new__ + else: + try: + enum_dict._new_args = _EnumArgSpec(first_enum.__new_member__) + except TypeError: + pass + elif not initial_flag: + if hasattr(first_enum, '__new_member__'): + enum_dict._new_args = _EnumArgSpec(first_enum.__new_member__) + if generate: + enum_dict['_generate_next_value_'] = generate + enum_dict._inherited_gnv = True + if init is not None: + if isinstance(init, basestring): + init = init.replace(',',' ').split() + enum_dict._init = init + elif hasattr(first_enum, '__new_member__'): + enum_dict._new_args = _EnumArgSpec(first_enum.__new_member__) + if order is not None: + enum_dict['_order_'] = staticmethod(order) + return enum_dict + + def __init__(cls, *args , **kwds): + pass + + def __new__(metacls, cls, bases, clsdict, init=None, start=None, settings=(), boundary=None, **kwds): + # handle py2 case first + if type(clsdict) is not _EnumDict: + # py2 and/or functional API gyrations + init = clsdict.pop('_init_', None) + start = clsdict.pop('_start_', None) + settings = clsdict.pop('_settings_', ()) + _order_ = clsdict.pop('_order_', clsdict.pop('__order__', None)) + _ignore_ = clsdict.pop('_ignore_', None) + _create_pseudo_member_ = clsdict.pop('_create_pseudo_member_', None) + _create_pseudo_member_values_ = clsdict.pop('_create_pseudo_member_values_', None) + _generate_next_value_ = clsdict.pop('_generate_next_value_', None) + _missing_ = clsdict.pop('_missing_', None) + _missing_value_ = clsdict.pop('_missing_value_', None) + _missing_name_ = clsdict.pop('_missing_name_', None) + _boundary_ = clsdict.pop('_boundary_', None) + _iter_member_ = clsdict.pop('_iter_member_', None) + _iter_member_by_value_ = clsdict.pop('_iter_member_by_value_', None) + _iter_member_by_def_ = clsdict.pop('_iter_member_by_def_', None) + __new__ = clsdict.pop('__new__', None) + __new__ = getattr(__new__, 'im_func', __new__) + __new__ = getattr(__new__, '__func__', __new__) + enum_members = dict([ + (k, v) for (k, v) in clsdict.items() + if not (_is_sunder(k) or _is_dunder(k) or _is_private_name(cls, k) or _is_descriptor(v)) + ]) + original_dict = clsdict + clsdict = metacls.__prepare__(cls, bases, init=init, start=start) + if settings: + clsdict['_settings_'] = settings + init = init or clsdict._init + if _order_ is None: + _order_ = clsdict.get('_order_') + if _order_ is not None: + _order_ = _order_.__get__(cls) + if isinstance(original_dict, OrderedDict): + calced_order = original_dict + elif _order_ is None: + calced_order = [name for (name, value) in enumsort(list(enum_members.items()))] + elif isinstance(_order_, basestring): + calced_order = _order_ = _order_.replace(',', ' ').split() + elif callable(_order_): + if init: + if not isinstance(init, basestring): + init = ' '.join(init) + member = NamedTuple('member', init and 'name ' + init or ['name', 'value']) + calced_order = [] + for name, value in enum_members.items(): + if init: + if not isinstance(value, tuple): + value = (value, ) + name_value = (name, ) + value + else: + name_value = tuple((name, value)) + if member._defined_len_ != len(name_value): + raise TypeError('%d values expected (%s), %d received (%s)' % ( + member._defined_len_, + ', '.join(member._fields_), + len(name_value), + ', '.join([repr(v) for v in name_value]), + )) + calced_order.append(member(*name_value)) + calced_order = [m.name for m in sorted(calced_order, key=_order_)] + else: + calced_order = _order_ + for name in ( + '_missing_', '_missing_value_', '_missing_name_', + '_ignore_', '_create_pseudo_member_', '_create_pseudo_member_values_', + '_generate_next_value_', '_order_', '__new__', + '_missing_', '_missing_value_', '_missing_name_', + '_boundary_', + '_iter_member_', '_iter_member_by_value_', '_iter_member_by_def_', + ): + attr = locals()[name] + if attr is not None: + clsdict[name] = attr + # now add members + for k in calced_order: + try: + clsdict[k] = original_dict[k] + except KeyError: + # this error will be handled when _order_ is checked + pass + for k, v in original_dict.items(): + if k not in calced_order: + clsdict[k] = v + del _order_, _ignore_, _create_pseudo_member_, _create_pseudo_member_values_, + del _generate_next_value_, _missing_, _missing_value_, _missing_name_ + # + # resume normal path + clsdict._locked = True + # + # check for illegal enum names (any others?) + member_names = clsdict._member_names + invalid_names = set(member_names) & set(['mro', '']) + if invalid_names: + raise ValueError('invalid enum member name(s): %s' % ( + ', '.join(invalid_names), )) + _order_ = clsdict.pop('_order_', None) + if isinstance(_order_, basestring): + _order_ = _order_.replace(',',' ').split() + init = clsdict._init + start = clsdict._start + settings = clsdict._settings + creating_init = [] + new_args = clsdict._new_args + auto_args = clsdict._auto_args + auto_init = False + if init is not None: + auto_init = True + creating_init = init[:] + if 'value' in creating_init and creating_init[0] != 'value': + raise TypeError("'value', if specified, must be the first item in 'init'") + magicvalue = MagicValue in settings + multivalue = MultiValue in settings + noalias = NoAlias in settings + unique = Unique in settings + # an Enum class cannot be mixed with other types (int, float, etc.) if + # it has an inherited __new__ unless a new __new__ is defined (or + # the resulting class will fail). + # an Enum class is final once enumeration items have been defined; + # + # remove any keys listed in _ignore_ + clsdict.setdefault('_ignore_', []).append('_ignore_') + ignore = clsdict['_ignore_'] + for key in ignore: + clsdict.pop(key, None) + # + boundary = boundary or clsdict.pop('_boundary_', None) + # convert to regular dict + clsdict = dict(clsdict.items()) + member_type, first_enum = metacls._get_mixins_(cls, bases) + # get the method to create enum members + __new__, save_new, new_uses_args = metacls._find_new_( + clsdict, + member_type, + first_enum, + ) + clsdict['_new_member_'] = staticmethod(__new__) + clsdict['_use_args_'] = new_uses_args + # + # convert future enum members into temporary _proto_members + # and record integer values in case this will be a Flag + flag_mask = 0 + for name in member_names: + value = test_value = clsdict[name] + if isinstance(value, auto) and value.value is not _auto_null: + test_value = value.value + if isinstance(test_value, baseinteger): + flag_mask |= test_value + if isinstance(test_value, tuple) and test_value and isinstance(test_value[0], baseinteger): + flag_mask |= test_value[0] + clsdict[name] = _proto_member(value) + # + # temp stuff + clsdict['_creating_init_'] = creating_init + clsdict['_multivalue_'] = multivalue + clsdict['_magicvalue_'] = magicvalue + clsdict['_noalias_'] = noalias + clsdict['_unique_'] = unique + # + # house-keeping structures + clsdict['_member_names_'] = [] + clsdict['_member_map_'] = OrderedDict() + clsdict['_member_type_'] = member_type + clsdict['_value2member_map_'] = {} + clsdict['_value2member_seq_'] = () + clsdict['_settings_'] = settings + clsdict['_start_'] = start + clsdict['_auto_init_'] = init + clsdict['_new_args_'] = new_args + clsdict['_auto_args_'] = auto_args + clsdict['_order_function_'] = None + # now set the __repr__ for the value + clsdict['_value_repr_'] = metacls._find_data_repr_(cls, bases) + # + # Flag structures (will be removed if final class is not a Flag + clsdict['_boundary_'] = ( + boundary + or getattr(first_enum, '_boundary_', None) + ) + clsdict['_flag_mask_'] = flag_mask + clsdict['_all_bits_'] = 2 ** ((flag_mask).bit_length()) - 1 + clsdict['_inverted_'] = None + # + # move skipped values out of the descriptor + for name, obj in clsdict.items(): + if isinstance(obj, nonmember): + clsdict[name] = obj.value + # + # If a custom type is mixed into the Enum, and it does not know how + # to pickle itself, pickle.dumps will succeed but pickle.loads will + # fail. Rather than have the error show up later and possibly far + # from the source, sabotage the pickle protocol for this class so + # that pickle.dumps also fails. + # + # However, if the new class implements its own __reduce_ex__, do not + # sabotage -- it's on them to make sure it works correctly. We use + # __reduce_ex__ instead of any of the others as it is preferred by + # pickle over __reduce__, and it handles all pickle protocols. + unpicklable = False + if '__reduce_ex__' not in clsdict: + if member_type is not object: + methods = ('__getnewargs_ex__', '__getnewargs__', + '__reduce_ex__', '__reduce__') + if not any(m in member_type.__dict__ for m in methods): + _make_class_unpicklable(clsdict) + unpicklable = True + # + # create a default docstring if one has not been provided + if '__doc__' not in clsdict: + clsdict['__doc__'] = 'An enumeration.' + # + # create our new Enum type + try: + exc = None + enum_class = type.__new__(metacls, cls, bases, clsdict) + except RuntimeError as e: + # any exceptions raised by _proto_member (aka member.__new__) will get converted to + # a RuntimeError, so get that original exception back and raise + # it instead + exc = e.__cause__ or e + if exc is not None: + raise exc + # + # if Python 3.5 or ealier, implement the __set_name__ and + # __init_subclass__ protocols + if pyver < PY3_6: + for name in member_names: + enum_class.__dict__[name].__set_name__(enum_class, name) + for name, obj in enum_class.__dict__.items(): + if name in member_names: + continue + if hasattr(obj, '__set_name__'): + obj.__set_name__(enum_class, name) + if Enum is not None: + super(enum_class, enum_class).__init_subclass__() + # + # double check that repr and friends are not the mixin's or various + # things break (such as pickle) + # + # Also, special handling for ReprEnum + if ReprEnum is not None and ReprEnum in bases: + if member_type is object: + raise TypeError( + 'ReprEnum subclasses must be mixed with a data type (i.e.' + ' int, str, float, etc.)' + ) + if '__format__' not in clsdict: + enum_class.__format__ = member_type.__format__ + clsdict['__format__'] = enum_class.__format__ + if '__str__' not in clsdict: + method = member_type.__str__ + if method is object.__str__: + # if member_type does not define __str__, object.__str__ will use + # its __repr__ instead, so we'll also use its __repr__ + method = member_type.__repr__ + enum_class.__str__ = method + clsdict['__str__'] = enum_class.__str__ + + for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'): + if name in clsdict: + # class has defined/imported/copied the method + continue + class_method = getattr(enum_class, name) + obj_method = getattr(member_type, name, None) + enum_method = getattr(first_enum, name, None) + if obj_method is not None and obj_method == class_method: + if name == '__reduce_ex__' and unpicklable: + continue + setattr(enum_class, name, enum_method) + clsdict[name] = enum_method + # + # for Flag, add __or__, __and__, __xor__, and __invert__ + if Flag is not None and issubclass(enum_class, Flag): + for name in ( + '__or__', '__and__', '__xor__', + '__ror__', '__rand__', '__rxor__', + '__invert__' + ): + if name not in clsdict: + setattr(enum_class, name, getattr(Flag, name)) + clsdict[name] = enum_method + # + # method resolution and int's are not playing nice + # Python's less than 2.6 use __cmp__ + if pyver < PY2_6: + # + if issubclass(enum_class, int): + setattr(enum_class, '__cmp__', getattr(int, '__cmp__')) + # + elif PY2: + # + if issubclass(enum_class, int): + for method in ( + '__le__', + '__lt__', + '__gt__', + '__ge__', + '__eq__', + '__ne__', + '__hash__', + ): + setattr(enum_class, method, getattr(int, method)) + # + # replace any other __new__ with our own (as long as Enum is not None, + # anyway) -- again, this is to support pickle + if Enum is not None: + # if the user defined their own __new__, save it before it gets + # clobbered in case they subclass later + if save_new: + setattr(enum_class, '__new_member__', enum_class.__dict__['__new__']) + setattr(enum_class, '__new__', Enum.__dict__['__new__']) + # + # _order_ checking is spread out into three/four steps + # - ensure _order_ is a list, not a string nor a function + # - if enum_class is a Flag: + # - remove any non-single-bit flags from _order_ + # - remove any aliases from _order_ + # - check that _order_ and _member_names_ match + # + # _order_ step 1: ensure _order_ is a list + if _order_: + if isinstance(_order_, staticmethod): + _order_ = _order_.__func__ + if callable(_order_): + # save order for future subclasses + enum_class._order_function_ = staticmethod(_order_) + # create ordered list for comparison + _order_ = [m.name for m in sorted(enum_class, key=_order_)] + # + # remove Flag structures if final class is not a Flag + if ( + Flag is None and cls != 'Flag' + or Flag is not None and not issubclass(enum_class, Flag) + ): + delattr(enum_class, '_boundary_') + delattr(enum_class, '_flag_mask_') + delattr(enum_class, '_all_bits_') + delattr(enum_class, '_inverted_') + elif Flag is not None and issubclass(enum_class, Flag): + # ensure _all_bits_ is correct and there are no missing flags + single_bit_total = 0 + multi_bit_total = 0 + for flag in enum_class._member_map_.values(): + if _is_single_bit(flag._value_): + single_bit_total |= flag._value_ + else: + # multi-bit flags are considered aliases + multi_bit_total |= flag._value_ + if enum_class._boundary_ is not KEEP: + missed = list(_iter_bits_lsb(multi_bit_total & ~single_bit_total)) + if missed: + raise TypeError( + 'invalid Flag %r -- missing values: %s' + % (cls, ', '.join((str(i) for i in missed))) + ) + enum_class._flag_mask_ = single_bit_total + enum_class._all_bits_ = 2 ** ((single_bit_total).bit_length()) - 1 + # + # set correct __iter__ + if [m._value_ for m in enum_class] != sorted([m._value_ for m in enum_class]): + enum_class._iter_member_ = enum_class._iter_member_by_def_ + if _order_: + # _order_ step 2: remove any items from _order_ that are not single-bit + _order_ = [ + o + for o in _order_ + if o not in enum_class._member_map_ or _is_single_bit(enum_class[o]._value_) + ] + # + # check for constants with auto() values + for k, v in enum_class.__dict__.items(): + if isinstance(v, constant) and isinstance(v.value, auto): + v.value = enum_class(v.value.value) + # + if _order_: + # _order_ step 3: remove aliases from _order_ + _order_ = [ + o + for o in _order_ + if ( + o not in enum_class._member_map_ + or + (o in enum_class._member_map_ and o in enum_class._member_names_) + )] + # _order_ step 4: verify that _order_ and _member_names_ match + if _order_ != enum_class._member_names_: + raise TypeError( + 'member order does not match _order_:\n%r\n%r' + % (enum_class._member_names_, _order_) + ) + return enum_class + + def __bool__(cls): + """ + classes/types should always be True. + """ + return True + + def __call__(cls, value=no_arg, names=None, module=None, qualname=None, type=None, start=1, boundary=None): + """Either returns an existing member, or creates a new enum class. + + This method is used both when an enum class is given a value to match + to an enumeration member (i.e. Color(3)) and for the functional API + (i.e. Color = Enum('Color', names='red green blue')). + + When used for the functional API: `module`, if set, will be stored in + the new class' __module__ attribute; `type`, if set, will be mixed in + as the first base class. + + Note: if `module` is not set this routine will attempt to discover the + calling module by walking the frame stack; if this is unsuccessful + the resulting class will not be pickleable. + """ + if names is None: # simple value lookup + return cls.__new__(cls, value) + # otherwise, functional API: we're creating a new Enum type + return cls._create_(value, names, module=module, qualname=qualname, type=type, start=start, boundary=boundary) + + def __contains__(cls, member): + if not isinstance(member, Enum): + raise TypeError("%r (%r) is not an " % (member, type(member))) + if not isinstance(member, cls): + return False + return True + + def __delattr__(cls, attr): + # nicer error message when someone tries to delete an attribute + # (see issue19025). + if attr in cls._member_map_: + raise AttributeError( + "%s: cannot delete Enum member %r." % (cls.__name__, attr), + ) + found_attr = _get_attr_from_chain(cls, attr) + if isinstance(found_attr, constant): + raise AttributeError( + "%s: cannot delete constant %r" % (cls.__name__, attr), + ) + elif isinstance(found_attr, property): + raise AttributeError( + "%s: cannot delete property %r" % (cls.__name__, attr), + ) + super(EnumType, cls).__delattr__(attr) + + def __dir__(cls): + interesting = set(cls._member_names_ + [ + '__class__', '__contains__', '__doc__', '__getitem__', + '__iter__', '__len__', '__members__', '__module__', + '__name__', + ]) + if cls._new_member_ is not object.__new__: + interesting.add('__new__') + if cls.__init_subclass__ is not Enum.__init_subclass__: + interesting.add('__init_subclass__') + if hasattr(object, '__qualname__'): + interesting.add('__qualname__') + for method in ('__init__', '__format__', '__repr__', '__str__'): + if getattr(cls, method) not in (getattr(Enum, method), getattr(Flag, method)): + interesting.add(method) + if cls._member_type_ is object: + return sorted(interesting) + else: + # return whatever mixed-in data type has + return sorted(set(dir(cls._member_type_)) | interesting) + + @_bltin_property + def __members__(cls): + """Returns a mapping of member name->value. + + This mapping lists all enum members, including aliases. Note that this + is a copy of the internal mapping. + """ + return cls._member_map_.copy() + + def __getitem__(cls, name): + try: + return cls._member_map_[name] + except KeyError: + exc = _sys.exc_info()[1] + if Flag is not None and issubclass(cls, Flag) and '|' in name: + try: + # may be an __or__ed name + result = cls(0) + for n in name.split('|'): + result |= cls[n] + return result + except KeyError: + raise exc + result = cls._missing_name_(name) + if isinstance(result, cls): + return result + else: + raise exc + + def __iter__(cls): + return (cls._member_map_[name] for name in cls._member_names_) + + def __reversed__(cls): + return (cls._member_map_[name] for name in reversed(cls._member_names_)) + + def __len__(cls): + return len(cls._member_names_) + + __nonzero__ = __bool__ + + def __repr__(cls): + return "" % (cls.__name__, ) + + def __setattr__(cls, name, value): + """Block attempts to reassign Enum members/constants. + + A simple assignment to the class namespace only changes one of the + several possible ways to get an Enum member from the Enum class, + resulting in an inconsistent Enumeration. + """ + member_map = cls.__dict__.get('_member_map_', {}) + if name in member_map: + raise AttributeError( + '%s: cannot rebind member %r.' % (cls.__name__, name), + ) + found_attr = _get_attr_from_chain(cls, name) + if isinstance(found_attr, constant): + raise AttributeError( + "%s: cannot rebind constant %r" % (cls.__name__, name), + ) + elif isinstance(found_attr, property): + raise AttributeError( + "%s: cannot rebind property %r" % (cls.__name__, name), + ) + super(EnumType, cls).__setattr__(name, value) + + def _convert(cls, *args, **kwds): + import warnings + warnings.warn("_convert is deprecated and will be removed, use" + " _convert_ instead.", DeprecationWarning, stacklevel=2) + return cls._convert_(*args, **kwds) + + def _convert_(cls, name, module, filter, source=None, boundary=None, as_global=False): + """ + Create a new Enum subclass that replaces a collection of global constants + """ + # convert all constants from source (or module) that pass filter() to + # a new Enum called name, and export the enum and its members back to + # module; + # also, replace the __reduce_ex__ method so unpickling works in + # previous Python versions + module_globals = vars(_sys.modules[module]) + if source: + source = vars(source) + else: + source = module_globals + members = [(key, source[key]) for key in source.keys() if filter(key)] + try: + # sort by value, name + members.sort(key=lambda t: (t[1], t[0])) + except TypeError: + # unless some values aren't comparable, in which case sort by just name + members.sort(key=lambda t: t[0]) + cls = cls(name, members, module=module, boundary=boundary or KEEP) + cls.__reduce_ex__ = _reduce_ex_by_name + if as_global: + global_enum(cls) + else: + module_globals.update(cls.__members__) + module_globals[name] = cls + return cls + + def _create_(cls, class_name, names, module=None, qualname=None, type=None, start=1, boundary=None): + """Convenience method to create a new Enum class. + + `names` can be: + + * A string containing member names, separated either with spaces or + commas. Values are auto-numbered from 1. + * An iterable of member names. Values are auto-numbered from 1. + * An iterable of (member name, value) pairs. + * A mapping of member name -> value. + """ + if PY2: + # if class_name is unicode, attempt a conversion to ASCII + if isinstance(class_name, unicode): + try: + class_name = class_name.encode('ascii') + except UnicodeEncodeError: + raise TypeError('%r is not representable in ASCII' % (class_name, )) + metacls = cls.__class__ + if type is None: + bases = (cls, ) + else: + bases = (type, cls) + _, first_enum = cls._get_mixins_(class_name, bases) + generate = getattr(first_enum, '_generate_next_value_', None) + generate = getattr(generate, 'im_func', generate) + # special processing needed for names? + if isinstance(names, basestring): + names = names.replace(',', ' ').split() + if isinstance(names, (tuple, list)) and names and isinstance(names[0], basestring): + original_names, names = names, [] + last_values = [] + for count, name in enumerate(original_names): + value = generate(name, start, count, last_values[:]) + last_values.append(value) + names.append((name, value)) + # Here, names is either an iterable of (name, value) or a mapping. + item = None # in case names is empty + clsdict = None + for item in names: + if clsdict is None: + # first time initialization + if isinstance(item, basestring): + clsdict = {} + else: + # remember the order + clsdict = metacls.__prepare__(class_name, bases) + if isinstance(item, basestring): + member_name, member_value = item, names[item] + else: + member_name, member_value = item + clsdict[member_name] = member_value + if clsdict is None: + # in case names was empty + clsdict = metacls.__prepare__(class_name, bases) + enum_class = metacls.__new__(metacls, class_name, bases, clsdict, boundary=boundary) + # TODO: replace the frame hack if a blessed way to know the calling + # module is ever developed + if module is None: + try: + module = _sys._getframe(2).f_globals['__name__'] + except (AttributeError, KeyError): + pass + if module is None: + _make_class_unpicklable(enum_class) + else: + enum_class.__module__ = module + if qualname is not None: + enum_class.__qualname__ = qualname + return enum_class + + @classmethod + def _check_for_existing_members_(mcls, class_name, bases): + if Enum is None: + return + for chain in bases: + for base in chain.__mro__: + if issubclass(base, Enum) and base._member_names_: + raise TypeError( + " cannot extend %r" + % (class_name, base) + ) + @classmethod + def _get_mixins_(mcls, class_name, bases): + """Returns the type for creating enum members, and the first inherited + enum class. + + bases: the tuple of bases that was given to __new__ + """ + if not bases or Enum is None: + return object, Enum + + mcls._check_for_existing_members_(class_name, bases) + + # ensure final parent class is an Enum derivative, find any concrete + # data type, and check that Enum has no members + first_enum = bases[-1] + if not issubclass(first_enum, Enum): + raise TypeError("new enumerations should be created as " + "`EnumName([mixin_type, ...] [data_type,] enum_type)`") + member_type = mcls._find_data_type_(class_name, bases) or object + if first_enum._member_names_: + raise TypeError("cannot extend enumerations via subclassing") + # + return member_type, first_enum + + @classmethod + def _find_data_repr_(mcls, class_name, bases): + for chain in bases: + for base in chain.__mro__: + if base is object: + continue + elif issubclass(base, Enum): + # if we hit an Enum, use it's _value_repr_ + return base._value_repr_ + elif '__repr__' in base.__dict__: + # this is our data repr + return base.__dict__['__repr__'] + return None + + @classmethod + def _find_data_type_(mcls, class_name, bases): + data_types = set() + for chain in bases: + candidate = None + for base in chain.__mro__: + if base is object or base is StdlibEnum or base is StdlibFlag: + continue + elif issubclass(base, Enum): + if base._member_type_ is not object: + data_types.add(base._member_type_) + elif '__new__' in base.__dict__: + if issubclass(base, Enum): + continue + elif StdlibFlag is not None and issubclass(base, StdlibFlag): + continue + data_types.add(candidate or base) + break + else: + candidate = candidate or base + if len(data_types) > 1: + raise TypeError('%r: too many data types: %r' % (class_name, data_types)) + elif data_types: + return data_types.pop() + else: + return None + + @staticmethod + def _get_settings_(bases): + """Returns the combined _settings_ of all Enum base classes + + bases: the tuple of bases given to __new__ + """ + settings = set() + for chain in bases: + for base in chain.__mro__: + if issubclass(base, Enum): + for s in base._settings_: + settings.add(s) + return settings + + @classmethod + def _find_new_(mcls, clsdict, member_type, first_enum): + """Returns the __new__ to be used for creating the enum members. + + clsdict: the class dictionary given to __new__ + member_type: the data type whose __new__ will be used by default + first_enum: enumeration to check for an overriding __new__ + """ + # now find the correct __new__, checking to see of one was defined + # by the user; also check earlier enum classes in case a __new__ was + # saved as __new_member__ + __new__ = clsdict.get('__new__', None) + # + # should __new__ be saved as __new_member__ later? + save_new = first_enum is not None and __new__ is not None + # + if __new__ is None: + # check all possibles for __new_member__ before falling back to + # __new__ + for method in ('__new_member__', '__new__'): + for possible in (member_type, first_enum): + target = getattr(possible, method, None) + if target not in ( + None, + None.__new__, + object.__new__, + Enum.__new__, + StdlibEnum.__new__, + ): + __new__ = target + break + if __new__ is not None: + break + else: + __new__ = object.__new__ + # if a non-object.__new__ is used then whatever value/tuple was + # assigned to the enum member name will be passed to __new__ and to the + # new enum member's __init__ + if __new__ is object.__new__: + new_uses_args = False + else: + new_uses_args = True + # + return __new__, save_new, new_uses_args + + + # In order to support Python 2 and 3 with a single + # codebase we have to create the Enum methods separately + # and then use the `type(name, bases, dict)` method to + # create the class. + +EnumMeta = EnumType + +enum_dict = _Addendum( + dict=EnumType.__prepare__('Enum', (object, )), + doc="Generic enumeration.\n\n Derive from this class to define new enumerations.\n\n", + ns=globals(), + ) + +@enum_dict +def __init__(self, *args, **kwds): + # auto-init method + _auto_init_ = self._auto_init_ + if _auto_init_ is None: + return + if 'value' in _auto_init_: + # remove 'value' from _auto_init_ as it has already been handled + _auto_init_ = _auto_init_[1:] + if _auto_init_: + if len(_auto_init_) < len(args): + raise TypeError('%d arguments expected (%s), %d received (%s)' + % (len(_auto_init_), _auto_init_, len(args), args)) + for name, arg in zip(_auto_init_, args): + setattr(self, name, arg) + if len(args) < len(_auto_init_): + remaining_args = _auto_init_[len(args):] + for name in remaining_args: + value = kwds.pop(name, undefined) + if value is undefined: + raise TypeError('missing value for: %r' % (name, )) + setattr(self, name, value) + if kwds: + # too many keyword arguments + raise TypeError('invalid keyword(s): %s' % ', '.join(kwds.keys())) + +@enum_dict +def __new__(cls, value): + # all enum instances are actually created during class construction + # without calling this method; this method is called by the metaclass' + # __call__ (i.e. Color(3) ), and by pickle + if NoAlias in cls._settings_: + raise TypeError('NoAlias enumerations cannot be looked up by value') + if type(value) is cls: + # For lookups like Color(Color.red) + # value = value.value + return value + # by-value search for a matching enum member + # see if it's in the reverse mapping (for hashable values) + try: + if value in cls._value2member_map_: + return cls._value2member_map_[value] + except TypeError: + # not there, now do long search -- O(n) behavior + for name, member in cls._value2member_seq_: + if name == value: + return member + # still not found -- try _missing_ hook + try: + exc = None + result = cls._missing_value_(value) + except Exception as e: + exc = e + result = None + if isinstance(result, cls) or getattr(cls, '_boundary_', None) is EJECT: + return result + else: + if value is no_arg: + ve_exc = ValueError('%s() should be called with a value' % (cls.__name__, )) + else: + ve_exc = ValueError("%r is not a valid %s" % (value, cls.__name__)) + if result is None and exc is None: + raise ve_exc + elif exc is None: + exc = TypeError( + 'error in %s._missing_: returned %r instead of None or a valid member' + % (cls.__name__, result) + ) + if not isinstance(exc, ValueError): + exc.__cause__ = ve_exc + raise exc + +@enum_dict +@classmethod +def __init_subclass__(cls, **kwds): + if pyver < PY3_6: + # end of the line + if kwds: + raise TypeError('unconsumed keyword arguments: %r' % (kwds, )) + else: + super(Enum, cls).__init_subclass__(**kwds) + +@enum_dict +@staticmethod +def _generate_next_value_(name, start, count, last_values, *args, **kwds): + for last_value in reversed(last_values): + try: + new_value = last_value + 1 + break + except TypeError: + pass + else: + new_value = start + if args: + return (new_value, ) + args + else: + return new_value + +@enum_dict +@classmethod +def _missing_(cls, value): + "deprecated, use _missing_value_ instead" + return None + +@enum_dict +@classmethod +def _missing_value_(cls, value): + "used for failed value access" + return cls._missing_(value) + +@enum_dict +@classmethod +def _missing_name_(cls, name): + "used for failed item access" + return None + +@enum_dict +def __repr__(self): + v_repr = self.__class__._value_repr_ or self._value_.__class__.__repr__ + return "<%s.%s: %s>" % (self.__class__.__name__, self._name_, v_repr(self._value_)) + +@enum_dict +def __str__(self): + return "%s.%s" % (self.__class__.__name__, self._name_) + +if PY3: + @enum_dict + def __dir__(self): + """ + Returns all members and all public methods + """ + if self.__class__._member_type_ is object: + interesting = set(['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'value']) + else: + interesting = set(object.__dir__(self)) + for name in getattr(self, '__dict__', []): + if name[0] != '_': + interesting.add(name) + for cls in self.__class__.mro(): + for name, obj in cls.__dict__.items(): + if name[0] == '_': + continue + if isinstance(obj, property): + # that's an enum.property + if obj.fget is not None or name not in self._member_map_: + interesting.add(name) + else: + # in case it was added by `dir(self)` + interesting.discard(name) + else: + interesting.add(name) + return sorted(interesting) + +@enum_dict +def __format__(self, format_spec): + # mixed-in Enums should use the mixed-in type's __format__, otherwise + # we can get strange results with the Enum name showing up instead of + # the value + + # pure Enum branch / overridden __str__ branch + overridden_str = self.__class__.__str__ != Enum.__str__ + if self._member_type_ is object or overridden_str: + cls = str + val = str(self) + # mix-in branch + else: + cls = self._member_type_ + val = self.value + return cls.__format__(val, format_spec) + +@enum_dict +def __hash__(self): + return hash(self._name_) + +@enum_dict +def __reduce_ex__(self, proto): + return self.__class__, (self._value_, ) + + +#################################### +# Python's less than 2.6 use __cmp__ + +if pyver < PY2_6: + + @enum_dict + def __cmp__(self, other): + if type(other) is self.__class__: + if self is other: + return 0 + return -1 + return NotImplemented + raise TypeError("unorderable types: %s() and %s()" % (self.__class__.__name__, other.__class__.__name__)) + +else: + + @enum_dict + def __le__(self, other): + raise TypeError("unorderable types: %s() <= %s()" % (self.__class__.__name__, other.__class__.__name__)) + + @enum_dict + def __lt__(self, other): + raise TypeError("unorderable types: %s() < %s()" % (self.__class__.__name__, other.__class__.__name__)) + + @enum_dict + def __ge__(self, other): + raise TypeError("unorderable types: %s() >= %s()" % (self.__class__.__name__, other.__class__.__name__)) + + @enum_dict + def __gt__(self, other): + raise TypeError("unorderable types: %s() > %s()" % (self.__class__.__name__, other.__class__.__name__)) + + +@enum_dict +def __eq__(self, other): + if type(other) is self.__class__: + return self is other + return NotImplemented + +@enum_dict +def __ne__(self, other): + if type(other) is self.__class__: + return self is not other + return NotImplemented + +@enum_dict +def __hash__(self): + return hash(self._name_) + +@enum_dict +def __reduce_ex__(self, proto): + return self.__class__, (self._value_, ) + + +# enum.property is used to provide access to the `name`, `value', etc., +# properties of enum members while keeping some measure of protection +# from modification, while still allowing for an enumeration to have +# members named `name`, `value`, etc.. This works because enumeration +# members are not set directly on the enum class -- enum.property will +# look them up in _member_map_. + +@enum_dict +@property +def name(self): + return self._name_ + +@enum_dict +@property +def value(self): + return self._value_ + +@enum_dict +@property +def values(self): + return self._values_ + +def _reduce_ex_by_name(self, proto): + return self.name + +Enum = EnumType('Enum', (object, ), enum_dict.resolve()) +del enum_dict + + # Enum has now been created + +class ReprEnum(Enum): + """ + Only changes the repr(), leaving str() and format() to the mixed-in type. + """ + + +class IntEnum(int, ReprEnum): + """ + Enum where members are also (and must be) ints + """ + + +class StrEnum(str, ReprEnum): + """ + Enum where members are also (and must already be) strings + + default value is member name, lower-cased + """ + + def __new__(cls, *values, **kwds): + if kwds: + raise TypeError('%r: keyword arguments not supported' % (cls.__name__)) + if values: + if not isinstance(values[0], str): + raise TypeError('%s: values must be str [%r is a %r]' % (cls.__name__, values[0], type(values[0]))) + value = str(*values) + member = str.__new__(cls, value) + member._value_ = value + return member + + __str__ = str.__str__ + + def _generate_next_value_(name, start, count, last_values): + """ + Return the lower-cased version of the member name. + """ + return name.lower() + + +class LowerStrEnum(StrEnum): + """ + Enum where members are also (and must already be) lower-case strings + + default value is member name, lower-cased + """ + + def __new__(cls, value, *args, **kwds): + obj = StrEnum.__new_member__(cls, value, *args, **kwds) + if value != value.lower(): + raise ValueError('%r is not lower-case' % value) + return obj + + +class UpperStrEnum(StrEnum): + """ + Enum where members are also (and must already be) upper-case strings + + default value is member name, upper-cased + """ + + def __new__(cls, value, *args, **kwds): + obj = StrEnum.__new_member__(cls, value, *args, **kwds) + if value != value.upper(): + raise ValueError('%r is not upper-case' % value) + return obj + + def _generate_next_value_(name, start, count, last_values, *args, **kwds): + return name.upper() + + +if PY3: + class AutoEnum(Enum): + """ + automatically use _generate_next_value_ when values are missing (Python 3 only) + """ + _settings_ = MagicValue + + +class AutoNumberEnum(Enum): + """ + Automatically assign increasing values to members. + + Py3: numbers match creation order + Py2: numbers are assigned alphabetically by member name + (unless `_order_` is specified) + """ + + def __new__(cls, *args, **kwds): + value = len(cls.__members__) + 1 + if cls._member_type_ is int: + obj = int.__new__(cls, value) + elif cls._member_type_ is long: + obj = long.__new__(cls, value) + else: + obj = object.__new__(cls) + obj._value_ = value + return obj + + +class AddValueEnum(Enum): + _settings_ = AddValue + + +class MultiValueEnum(Enum): + """ + Multiple values can map to each member. + """ + _settings_ = MultiValue + + +class NoAliasEnum(Enum): + """ + Duplicate value members are distinct, but cannot be looked up by value. + """ + _settings_ = NoAlias + + +class OrderedEnum(Enum): + """ + Add ordering based on values of Enum members. + """ + + def __ge__(self, other): + if self.__class__ is other.__class__: + return self._value_ >= other._value_ + return NotImplemented + + def __gt__(self, other): + if self.__class__ is other.__class__: + return self._value_ > other._value_ + return NotImplemented + + def __le__(self, other): + if self.__class__ is other.__class__: + return self._value_ <= other._value_ + return NotImplemented + + def __lt__(self, other): + if self.__class__ is other.__class__: + return self._value_ < other._value_ + return NotImplemented + + +if sqlite3: + class SqliteEnum(Enum): + def __conform__(self, protocol): + if protocol is sqlite3.PrepareProtocol: + return self.name + + +class UniqueEnum(Enum): + """ + Ensure no duplicate values exist. + """ + _settings_ = Unique + + +def convert(enum, name, module, filter, source=None): + """ + Create a new Enum subclass that replaces a collection of global constants + + enum: Enum, IntEnum, ... + name: name of new Enum + module: name of module (__name__ in global context) + filter: function that returns True if name should be converted to Enum member + source: namespace to check (defaults to 'module') + """ + # convert all constants from source (or module) that pass filter() to + # a new Enum called name, and export the enum and its members back to + # module; + # also, replace the __reduce_ex__ method so unpickling works in + # previous Python versions + module_globals = vars(_sys.modules[module]) + if source: + source = vars(source) + else: + source = module_globals + members = dict((name, value) for name, value in source.items() if filter(name)) + enum = enum(name, members, module=module) + enum.__reduce_ex__ = _reduce_ex_by_name + module_globals.update(enum.__members__) + module_globals[name] = enum + +def extend_enum(enumeration, name, *args, **kwds): + """ + Add a new member to an existing Enum. + """ + # there are four possibilities: + # - extending an aenum Enum or 3.11+ enum Enum + # - extending an aenum Flag or 3.11+ enum Flag + # - extending a pre-3.11 stdlib Enum Flag + # - extending a 3.11+ stdlib Flag + # + # fail early if name is already in the enumeration + if ( + name in enumeration.__dict__ + or name in enumeration._member_map_ + or name in [t[1] for t in getattr(enumeration, '_value2member_seq_', ())] + ): + raise TypeError('%r already in use as %r' % (name, enumeration.__dict__.get(name, enumeration[name]))) + # and check for other instances in parent classes + descriptor = None + for base in enumeration.__mro__[1:]: + descriptor = base.__dict__.get(name) + if descriptor is not None: + if isinstance(descriptor, (property, DynamicClassAttribute)): + break + else: + raise TypeError('%r already in use in superclass %r' % (name, base.__name__)) + try: + _member_map_ = enumeration._member_map_ + _member_names_ = enumeration._member_names_ + _member_type_ = enumeration._member_type_ + _value2member_map_ = enumeration._value2member_map_ + base_attributes = set([a for b in enumeration.mro() for a in b.__dict__]) + except AttributeError: + raise TypeError('%r is not a supported Enum' % (enumeration, )) + try: + _value2member_seq_ = enumeration._value2member_seq_ + _multi_value_ = MultiValue in enumeration._settings_ + _no_alias_ = NoAlias in enumeration._settings_ + _unique_ = Unique in enumeration._settings_ + _auto_init_ = enumeration._auto_init_ or [] + except AttributeError: + # standard Enum + _value2member_seq_ = [] + _multi_value_ = False + _no_alias_ = False + _unique_ = False + _auto_init_ = [] + if _multi_value_ and not args: + # must specify values for multivalue enums + raise ValueError('no values specified for MultiValue enum %r' % enumeration.__name__) + mt_new = _member_type_.__new__ + _new = getattr(enumeration, '__new_member__', mt_new) + if not args: + last_values = [m.value for m in enumeration] + count = len(enumeration) + start = getattr(enumeration, '_start_', None) + if start is None: + start = last_values and (last_values[-1] + 1) or 1 + _gnv = getattr(enumeration, '_generate_next_value_', None) + if _gnv is not None: + args = ( _gnv(name, start, count, last_values), ) + else: + # must be a 3.4 or 3.5 Enum + args = (start, ) + if _new is object.__new__: + new_uses_args = False + else: + new_uses_args = True + if len(args) == 1: + [value] = args + else: + value = args + more_values = () + kwds = {} + if isinstance(value, enum): + args = value.args + kwds = value.kwds + if not isinstance(value, tuple): + args = (value, ) + else: + args = value + # tease value out of auto-init if specified + if 'value' in _auto_init_: + if 'value' in kwds: + value = kwds.pop('value') + else: + value, args = args[0], args[1:] + elif _multi_value_: + value, more_values, args = args[0], args[1:], () + if new_uses_args: + args = (value, ) + if _member_type_ is tuple: + args = (args, ) + if not new_uses_args: + new_member = _new(enumeration) + if not hasattr(new_member, '_value_'): + new_member._value_ = value + else: + new_member = _new(enumeration, *args, **kwds) + if not hasattr(new_member, '_value_'): + new_member._value_ = _member_type_(*args) + value = new_member._value_ + if _multi_value_: + if 'value' in _auto_init_: + args = more_values + else: + # put all the values back into args for the init call + args = (value, ) + more_values + new_member._name_ = name + new_member.__objclass__ = enumeration.__class__ + new_member.__init__(*args) + new_member._values_ = (value, ) + more_values + # do final checks before modifying enum structures: + # - is new member a flag? + # - does the new member fit in the enum's declared _boundary_? + # - is new member an alias? + # + _all_bits_ = _flag_mask_ = None + if hasattr(enumeration, '_all_bits_'): + _all_bits_ = enumeration._all_bits_ | value + _flag_mask_ = enumeration._flag_mask_ | value + if enumeration._boundary_ != 'keep': + missed = list(_iter_bits_lsb(_flag_mask_ & ~_all_bits_)) + if missed: + raise TypeError( + 'invalid Flag %r -- missing values: %s' + % (cls, ', '.join((str(i) for i in missed))) + ) + # If another member with the same value was already defined, the + # new member becomes an alias to the existing one. + if _no_alias_: + # unless NoAlias was specified + return _finalize_extend_enum(enumeration, new_member, bits=_all_bits_, mask=_flag_mask_) + else: + # handle "normal" aliases + new_values = new_member._values_ + for canonical_member in _member_map_.values(): + canonical_values_ = getattr(canonical_member, '_values_', [canonical_member._value_]) + for canonical_value in canonical_values_: + for new_value in new_values: + if canonical_value == new_value: + # name is an alias + if _unique_ or _multi_value_: + # aliases not allowed in Unique and MultiValue enums + raise ValueError('%r is a duplicate of %r' % (new_member, canonical_member)) + else: + # aliased name can be added, remaining checks irrelevant + # aliases don't appear in member names (only in __members__ and _member_map_). + return _finalize_extend_enum(enumeration, canonical_member, name=name, bits=_all_bits_, mask=_flag_mask_, is_alias=True) + # not a standard alias, but maybe a flag alias + if pyver < PY3_6: + flag_bases = Flag, + else: + flag_bases = Flag, StdlibFlag + if issubclass(enumeration, flag_bases) and hasattr(enumeration, '_all_bits_'): + # handle the new flag type + if _is_single_bit(value): + # a new member! (an aliase would have been discovered in the previous loop) + return _finalize_extend_enum(enumeration, new_member, bits=_all_bits_, mask=_flag_mask_) + else: + # might be an 3.11 Flag alias + if value & enumeration._flag_mask_ == value and _value2member_map_.get(value) is not None: + # yup, it's an alias to existing members... and its an alias of an alias + canonical = _value2member_map_.get(value) + return _finalize_extend_enum(enumeration, canonical, name=name, bits=_all_bits_, mask=_flag_mask_, is_alias=True) + else: + return _finalize_extend_enum(enumeration, new_member, bits=_all_bits_, mask=_flag_mask_, is_alias=True) + else: + # if we get here, we have a brand new member + return _finalize_extend_enum(enumeration, new_member) + +def _finalize_extend_enum(enumeration, new_member, name=None, bits=None, mask=None, is_alias=False): + name = name or new_member.name + descriptor = None + for base in enumeration.__mro__[1:]: + descriptor = base.__dict__.get(name) + if descriptor is not None: + if isinstance(descriptor, (property, DynamicClassAttribute)): + break + else: + raise TypeError('%r already in use in superclass %r' % (name, base.__name__)) + if not descriptor: + # get redirect in place before adding to _member_map_ + redirect = property() + redirect.__set_name__(enumeration, name) + setattr(enumeration, name, redirect) + if not is_alias: + enumeration._member_names_.append(name) + enumeration._member_map_[name] = new_member + for v in getattr(new_member, '_values_', [new_member._value_]): + try: + enumeration._value2member_map_[v] = new_member + except TypeError: + enumeration._value2member_seq_ += ((v, new_member), ) + if bits: + enumeration._all_bits_ = bits + enumeration._flag_mask_ = mask + return new_member + +def unique(enumeration): + """ + Class decorator that ensures only unique members exist in an enumeration. + """ + duplicates = [] + for name, member in enumeration.__members__.items(): + if name != member.name: + duplicates.append((name, member.name)) + if duplicates: + duplicate_names = ', '.join( + ["%s -> %s" % (alias, name) for (alias, name) in duplicates] + ) + raise ValueError('duplicate names found in %r: %s' % + (enumeration, duplicate_names) + ) + return enumeration + +# Flag + +@export(globals()) +class FlagBoundary(StrEnum): + """ + control how out of range values are handled + "strict" -> error is raised [default] + "conform" -> extra bits are discarded + "eject" -> lose flag status (becomes a normal integer) + """ + STRICT = auto() + CONFORM = auto() + EJECT = auto() + KEEP = auto() +assert FlagBoundary.STRICT == 'strict', (FlagBoundary.STRICT, FlagBoundary.CONFORM) + +class Flag(Enum): + """ + Generic flag enumeration. + + Derive from this class to define new flag enumerations. + """ + + _boundary_ = STRICT + _numeric_repr_ = repr + + + def _generate_next_value_(name, start, count, last_values, *args, **kwds): + """ + Generate the next value when not given. + + name: the name of the member + start: the initital start value or None + count: the number of existing members + last_value: the last value assigned or None + """ + if not count: + if args: + return ((1, start)[start is not None], ) + args + else: + return (1, start)[start is not None] + else: + last_value = max(last_values) + try: + high_bit = _high_bit(last_value) + result = 2 ** (high_bit+1) + if args: + return (result,) + args + else: + return result + except Exception: + pass + raise TypeError('invalid Flag value: %r' % last_value) + + @classmethod + def _iter_member_by_value_(cls, value): + """ + Extract all members from the value in definition (i.e. increasing value) order. + """ + for val in _iter_bits_lsb(value & cls._flag_mask_): + yield cls._value2member_map_.get(val) + + _iter_member_ = _iter_member_by_value_ + + @classmethod + def _iter_member_by_def_(cls, value): + """ + Extract all members from the value in definition order. + """ + members = list(cls._iter_member_by_value_(value)) + members.sort(key=lambda m: m._sort_order_) + for member in members: + yield member + + @classmethod + def _missing_(cls, value): + """ + return a member matching the given value, or None + """ + return cls._create_pseudo_member_(value) + + @classmethod + def _create_pseudo_member_(cls, *values): + """ + Create a composite member. + """ + value = values[0] + if not isinstance(value, baseinteger): + raise ValueError( + "%r is not a valid %s" % (value, getattr(cls, '__qualname__', cls.__name__)) + ) + # check boundaries + # - value must be in range (e.g. -16 <-> +15, i.e. ~15 <-> 15) + # - value must not include any skipped flags (e.g. if bit 2 is not + # defined, then 0d10 is invalid) + neg_value = None + if ( + not ~cls._all_bits_ <= value <= cls._all_bits_ + or value & (cls._all_bits_ ^ cls._flag_mask_) + ): + if cls._boundary_ is STRICT: + max_bits = max(value.bit_length(), cls._flag_mask_.bit_length()) + raise ValueError( + "%s: invalid value: %r\n given %s\n allowed %s" + % (cls.__name__, value, bin(value, max_bits), bin(cls._flag_mask_, max_bits)) + ) + elif cls._boundary_ is CONFORM: + value = value & cls._flag_mask_ + elif cls._boundary_ is EJECT: + return value + elif cls._boundary_ is KEEP: + if value < 0: + value = ( + max(cls._all_bits_+1, 2**(value.bit_length())) + + value + ) + else: + raise ValueError( + 'unknown flag boundary: %r' % (cls._boundary_, ) + ) + if value < 0: + neg_value = value + value = cls._all_bits_ + 1 + value + # get members and unknown + unknown = value & ~cls._flag_mask_ + members = list(cls._iter_member_(value)) + if unknown and cls._boundary_ is not KEEP: + raise ValueError( + '%s(%r) --> unknown values %r [%s]' + % (cls.__name__, value, unknown, bin(unknown)) + ) + # let class adjust values + values = cls._create_pseudo_member_values_(members, *values) + __new__ = getattr(cls, '__new_member__', None) + if cls._member_type_ is object and not __new__: + # construct a singleton enum pseudo-member + pseudo_member = object.__new__(cls) + else: + pseudo_member = (__new__ or cls._member_type_.__new__)(cls, *values) + if not hasattr(pseudo_member, 'value'): + pseudo_member._value_ = value + if members: + pseudo_member._name_ = '|'.join([m._name_ for m in members]) + if unknown: + pseudo_member._name_ += '|%s' % cls._numeric_repr_(unknown) + else: + pseudo_member._name_ = None + # use setdefault in case another thread already created a composite + # with this value, but only if all members are known + # note: zero is a special case -- add it + if not unknown: + pseudo_member = cls._value2member_map_.setdefault(value, pseudo_member) + if neg_value is not None: + cls._value2member_map_[neg_value] = pseudo_member + return pseudo_member + + + @classmethod + def _create_pseudo_member_values_(cls, members, *values): + """ + Return values to be fed to __new__ to create new member. + """ + if cls._member_type_ in (baseinteger + (object, )): + return values + elif len(values) < 2: + return values + (cls._member_type_(), ) + else: + return values + + def __contains__(self, other): + """ + Returns True if self has at least the same flags set as other. + """ + if not isinstance(other, self.__class__): + raise TypeError( + "unsupported operand type(s) for 'in': '%s' and '%s'" % ( + type(other).__name__, self.__class__.__name__)) + if other._value_ == 0 or self._value_ == 0: + return False + return other._value_ & self._value_ == other._value_ + + def __iter__(self): + """ + Returns flags in definition order. + """ + for member in self._iter_member_(self._value_): + yield member + + def __len__(self): + return _bit_count(self._value_) + + def __repr__(self): + cls = self.__class__ + if self._name_ is None: + # only zero is unnamed by default + return '<%s: %r>' % (cls.__name__, self._value_) + else: + return '<%s.%s: %r>' % (cls.__name__, self._name_, self._value_) + + def __str__(self): + cls = self.__class__ + if self._name_ is None: + return '%s(%s)' % (cls.__name__, self._value_) + else: + return '%s.%s' % (cls.__name__, self._name_) + + if PY2: + def __nonzero__(self): + return bool(self._value_) + else: + def __bool__(self): + return bool(self._value_) + + def __or__(self, other): + if isinstance(other, self.__class__): + other_value = other._value_ + elif self._member_type_ is not object and isinstance(other, self._member_type_): + other_value = other + else: + return NotImplemented + return self.__class__(self._value_ | other_value) + + def __and__(self, other): + if isinstance(other, self.__class__): + other_value = other._value_ + elif self._member_type_ is not object and isinstance(other, self._member_type_): + other_value = other + else: + return NotImplemented + return self.__class__(self._value_ & other_value) + + def __xor__(self, other): + if isinstance(other, self.__class__): + other_value = other._value_ + elif self._member_type_ is not object and isinstance(other, self._member_type_): + other_value = other + else: + return NotImplemented + return self.__class__(self._value_ ^ other_value) + + def __invert__(self): + if self._inverted_ is None: + if self._boundary_ is KEEP: + # use all bits + self._inverted_ = self.__class__(~self._value_) + else: + # calculate flags not in this member + self._inverted_ = self.__class__(self._flag_mask_ ^ self._value_) + self._inverted_._inverted_ = self + return self._inverted_ + + __ror__ = __or__ + __rand__ = __and__ + __rxor__ = __xor__ + + + +class IntFlag(int, ReprEnum, Flag): + """Support for integer-based Flags""" + + _boundary_ = EJECT + + +def _high_bit(value): + """returns index of highest bit, or -1 if value is zero or negative""" + return value.bit_length() - 1 + +def global_enum_repr(self): + """ + use module.enum_name instead of class.enum_name + + the module is the last module in case of a multi-module name + """ + module = self.__class__.__module__.split('.')[-1] + return '%s.%s' % (module, self._name_) + +def global_flag_repr(self): + """ + use module.flag_name instead of class.flag_name + + the module is the last module in case of a multi-module name + """ + module = self.__class__.__module__.split('.')[-1] + cls_name = self.__class__.__name__ + if self._name_ is None: + return "%s.%s(%r)" % (module, cls_name, self._value_) + if _is_single_bit(self): + return '%s.%s' % (module, self._name_) + if self._boundary_ is not FlagBoundary.KEEP: + return '|'.join(['%s.%s' % (module, name) for name in self.name.split('|')]) + else: + name = [] + for n in self._name_.split('|'): + if n[0].isdigit(): + name.append(n) + else: + name.append('%s.%s' % (module, n)) + return '|'.join(name) + +def global_str(self): + """ + use enum_name instead of class.enum_name + """ + if self._name_ is None: + return "%s(%r)" % (cls_name, self._value_) + else: + return self._name_ + +def global_enum(cls, update_str=False): + """ + decorator that makes the repr() of an enum member reference its module + instead of its class; also exports all members to the enum's module's + global namespace + """ + if issubclass(cls, Flag): + cls.__repr__ = global_flag_repr + else: + cls.__repr__ = global_enum_repr + if not issubclass(cls, ReprEnum) or update_str: + cls.__str__ = global_str + _sys.modules[cls.__module__].__dict__.update(cls.__members__) + return cls + + +class module(object): + + def __init__(self, cls, *args): + self.__name__ = cls.__name__ + self._parent_module = cls.__module__ + self.__all__ = [] + all_objects = cls.__dict__ + if not args: + args = [k for k, v in all_objects.items() if isinstance(v, (NamedConstant, Enum))] + for name in args: + self.__dict__[name] = all_objects[name] + self.__all__.append(name) + + def register(self): + _sys.modules["%s.%s" % (self._parent_module, self.__name__)] = self + +if StdlibEnumMeta: + + from _weakrefset import WeakSet + + def __subclasscheck__(cls, subclass): + """ + Override for issubclass(subclass, cls). + """ + if not isinstance(subclass, type): + raise TypeError('issubclass() arg 1 must be a class (got %r)' % (subclass, )) + # Check cache + try: + cls.__dict__['_subclass_cache_'] + except KeyError: + cls._subclass_cache_ = WeakSet() + cls._subclass_negative_cache_ = WeakSet() + except RecursionError: + import sys + exc, cls, tb = sys.exc_info() + exc = RecursionError('possible causes for endless recursion:\n - __getattribute__ is not ignoring __dunder__ attibutes\n - __instancecheck__ and/or __subclasscheck_ are (mutually) recursive\n see `aenum.remove_stdlib_integration` for temporary work-around') + raise_from_none(exc) + if subclass in cls._subclass_cache_: + return True + # Check negative cache + elif subclass in cls._subclass_negative_cache_: + return False + if cls is subclass: + cls._subclass_cache_.add(subclass) + return True + # Check if it's a direct subclass + if cls in getattr(subclass, '__mro__', ()): + cls._subclass_cache_.add(subclass) + return True + # Check if it's an aenum.Enum|IntEnum|IntFlag|Flag subclass + if cls is StdlibIntFlag and issubclass(subclass, IntFlag): + cls._subclass_cache_.add(subclass) + return True + elif cls is StdlibFlag and issubclass(subclass, Flag): + cls._subclass_cache_.add(subclass) + return True + elif cls is StdlibIntEnum and issubclass(subclass, IntEnum): + cls._subclass_cache_.add(subclass) + return True + if cls is StdlibEnum and issubclass(subclass, Enum): + cls._subclass_cache_.add(subclass) + return True + # No dice; update negative cache + cls._subclass_negative_cache_.add(subclass) + return False + + def __instancecheck__(cls, instance): + subclass = instance.__class__ + try: + return cls.__subclasscheck__(subclass) + except RecursionError: + import sys + exc, cls, tb = sys.exc_info() + exc = RecursionError('possible causes for endless recursion:\n - __getattribute__ is not ignoring __dunder__ attibutes\n - __instancecheck__ and/or __subclasscheck_ are (mutually) recursive\n see `aenum.remove_stdlib_integration` for temporary work-around') + raise_from_none(exc) + + StdlibEnumMeta.__subclasscheck__ = __subclasscheck__ + StdlibEnumMeta.__instancecheck__ = __instancecheck__ + +def add_stdlib_integration(): + if StdlibEnum: + StdlibEnumMeta.__subclasscheck__ = __subclasscheck__ + StdlibEnumMeta.__instancecheck__ = __instancecheck__ + +def remove_stdlib_integration(): + """ + Remove the __instancecheck__ and __subclasscheck__ overrides from the stdlib Enum. + + Those overrides are in place so that code detecting stdlib enums will also detect + aenum enums. If a buggy __getattribute__, __instancecheck__, or __subclasscheck__ + is defined on a custom EnumMeta then RecursionErrors can result; using this + function after importing aenum will solve that problem, but the better solution is + to fix the buggy method. + """ + if StdlibEnum: + del StdlibEnumMeta.__instancecheck__ + del StdlibEnumMeta.__subclasscheck__ + diff --git a/venv/Lib/site-packages/aenum/__pycache__/__init__.cpython-39.pyc b/venv/Lib/site-packages/aenum/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..176f78e250e4099e3c3b3bd6e90f89417d332065 GIT binary patch literal 104964 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 + +---------------- + +An ``Enum`` is a set of symbolic names (members) bound to unique, constant +values. Within an enumeration, the members can be compared by identity, and +the enumeration itself can be iterated over. + +A ``NamedTuple`` is a class-based, fixed-length tuple with a name for each +possible position accessible using attribute-access notation. + +A ``NamedConstant`` is a class whose members cannot be rebound; it lacks all +other ``Enum`` capabilities, however; consequently, it can have duplicate +values. There is also a ``module`` function that can insert the +``NamedConstant`` class into ``sys.modules`` where it will appear to be a +module whose top-level names cannot be rebound. + +.. note:: + ``constant`` refers to names not being rebound; mutable objects can be + mutated. + + +Module Contents +--------------- + +This module defines five enumeration classes that can be used to define unique +sets of names and values, one ``Enum`` class decorator, one ``NamedTuple`` +class, one ``NamedConstant`` class, and several helpers. + +``NamedConstant`` + +NamedConstant class for creating groups of constants. These names cannot be +rebound to other values. + +``Enum`` + +Base class for creating enumerated constants. See section `Enum Functional API`_ +for an alternate construction syntax. + +``AddValue`` + +Flag specifying that ``_generate_next_value_`` should always be called to +provide the initial value for an enum member. + +``MultiValue`` + +Flag specifying that each item of tuple value is a separate value for that +member; the first tuple item is the canonical one. + +``NoAlias`` + +Flag specifying that duplicate valued members are distinct and not aliases; +by-value lookups are disabled. + +``Unique`` + +Flag specifying that duplicate valued members are not allowed. + +.. note:: + The flags are inherited by the enumeration's subclasses. To use them in + Python 2 assign to ``_settings_`` in the class body. + +``IntEnum`` + +Base class for creating enumerated constants that are also subclasses of ``int``. + +``AutoNumberEnum`` + +Derived class that automatically assigns an ``int`` value to each member. + +``OrderedEnum`` + +Derived class that adds ``<``, ``<=``, ``>=``, and ``>`` methods to an ``Enum``. + +``UniqueEnum`` + +Derived class that ensures only one name is bound to any one value. + +``unique`` + +Enum class decorator that ensures only one name is bound to any one value. + +.. note:: + + the ``UniqueEnum`` class, the ``unique`` decorator, and the Unique + flag all do the same thing; you do not need to use more than one of + them at the same time. + +``NamedTuple`` + +Base class for `creating NamedTuples`_, either by subclassing or via it's +functional API. + +``constant`` + +Descriptor to add constant values to an ``Enum``, or advanced constants to +``NamedConstant``. + +``convert`` + +Helper to transform target global variables into an ``Enum``. + +``enum`` + +Helper for specifying keyword arguments when creating ``Enum`` members. + +``export`` + +Helper for inserting ``Enum`` members and ``NamedConstant`` constants into a +namespace (usually ``globals()``. + +``extend_enum`` + +Helper for adding new ``Enum`` members, both stdlib and aenum. + +``module`` + +Function to take a ``NamedConstant`` or ``Enum`` class and insert it into +``sys.modules`` with the affect of a module whose top-level constant and +member names cannot be rebound. + +``skip`` + +Descriptor to add a normal (non-``Enum`` member) attribute to an ``Enum`` +or ``NamedConstant``. + + +Creating an Enum +---------------- + +Enumerations are created using the ``class`` syntax, which makes them +easy to read and write. An alternative creation method is described in +`Enum Functional API`_. To define an enumeration, subclass ``Enum`` as +follows:: + + >>> from aenum import Enum + >>> class Color(Enum): + ... red = 1 + ... green = 2 + ... blue = 3 + +*Nomenclature* + + - The class ``Color`` is an *enumeration* (or *enum*) + - The attributes ``Color.red``, ``Color.green``, etc., are + *enumeration members* (or *enum members*). + - The enum members have *names* and *values* (the name of + ``Color.red`` is ``red``, the value of ``Color.blue`` is + ``3``, etc.) + +.. note:: + + Even though we use the ``class`` syntax to create Enums, Enums + are not normal Python classes. See `How are Enums different?`_ for + more details. + +Enumeration members have human readable string representations:: + + >>> print(Color.red) + Color.red + +...while their ``repr`` has more information:: + + >>> print(repr(Color.red)) + + +The *type* of an enumeration member is the enumeration it belongs to:: + + >>> type(Color.red) + + >>> isinstance(Color.green, Color) + True + +Enumerations support iteration. In Python 3.x definition order is used; in +Python 2.x the definition order is not available, but class attribute +``_order_`` is supported; otherwise, value order is used if posible, +otherwise alphabetical name order is used:: + + >>> class Shake(Enum): + ... _order_ = 'vanilla chocolate cookies mint' # only needed in 2.x + ... vanilla = 7 + ... chocolate = 4 + ... cookies = 9 + ... mint = 3 + ... + >>> for shake in Shake: + ... print(shake) + ... + Shake.vanilla + Shake.chocolate + Shake.cookies + Shake.mint + +The ``_order_`` attribute is always removed, but in 3.x it is also used to +verify that definition order is the same (useful for py2&3 code bases); +however, in the stdlib version it will be ignored and not removed. + +.. note:: + + To maintain compatibility with Python 3.4 and 3.5, use __order__ + instead (double leading and trailing underscores). + +Enumeration members are hashable, so they can be used in dictionaries and sets:: + + >>> apples = {} + >>> apples[Color.red] = 'red delicious' + >>> apples[Color.green] = 'granny smith' + >>> apples == {Color.red: 'red delicious', Color.green: 'granny smith'} + True + +In Python 3 the class syntax has a few extra advancements:: + + --> class Color( + ... Enum, + ... settings=(AddValue, MultiValue, NoAlias, Unique), + ... init='field_name1 field_name2 ...', + ... start=7, + ... ) + ... + +``start`` is used to specify the starting value for the first member:: + + --> class Count(Enum, start=11): + ... eleven + ... twelve + ... + --> Count.twelve.value == 12 + True + +``init`` specifies the attribute names to store creation values to:: + + --> class Planet(Enum, init='mass radius'): + ... MERCURY = (3.303e+23, 2.4397e6) + ... EARTH = (5.976e+24, 6.37814e6) + ... + --> Planet.EARTH.value + (5.976e+24, 6378140.0) + --> Planet.EARTH.radius + 2.4397e6 + +The various settings enable special behavior: + +- ``AddValue`` calls a user supplied ``_generate_next_value_`` to provide + the initial value +- ``MultiValue`` allows multiple values per member instead of the usual 1 +- ``NoAlias`` allows different members to have the same value +- ``Unique`` disallows different members to have the same value + +.. note:: + + To use these features in Python 2 use the _sundered_ versions of + the names in the class body: ``_start_``, ``_init_``, ``_settings_``. + + +Programmatic access to enumeration members and their attributes +--------------------------------------------------------------- + +Sometimes it's useful to access members in enumerations programmatically (i.e. +situations where ``Color.red`` won't do because the exact color is not known +at program-writing time). ``Enum`` allows such access:: + + >>> Color(1) + + >>> Color(3) + + +If you want to access enum members by *name*, use item access:: + + >>> Color['red'] + + >>> Color['green'] + + +If have an enum member and need its ``name`` or ``value``:: + + >>> member = Color.red + >>> member.name + 'red' + >>> member.value + 1 + + +Duplicating enum members and values +----------------------------------- + +Having two enum members (or any other attribute) with the same name is invalid; +in Python 3.x this would raise an error, but in Python 2.x the second member +simply overwrites the first:: + + # python 2.x + --> class Shape(Enum): + ... square = 2 + ... square = 3 + ... + --> Shape.square + + + # python 3.x + --> class Shape(Enum): + ... square = 2 + ... square = 3 + Traceback (most recent call last): + ... + TypeError: Attempted to reuse key: 'square' + +However, two enum members are allowed to have the same value. Given two members +A and B with the same value (and A defined first), B is an alias to A. By-value +lookup of the value of A and B will return A. By-name lookup of B will also +return A:: + + >>> class Shape(Enum): + ... _order_ = 'square diamond circle' # needed in 2.x + ... square = 2 + ... diamond = 1 + ... circle = 3 + ... alias_for_square = 2 + ... + >>> Shape.square + + >>> Shape.alias_for_square + + >>> Shape(2) + + + +Allowing aliases is not always desirable. ``unique`` can be used to ensure +that none exist in a particular enumeration:: + + >>> from aenum import unique + >>> @unique + ... class Mistake(Enum): + ... _order_ = 'one two three' # only needed in 2.x + ... one = 1 + ... two = 2 + ... three = 3 + ... four = 3 + Traceback (most recent call last): + ... + ValueError: duplicate names found in : four -> three + +Iterating over the members of an enum does not provide the aliases:: + + >>> list(Shape) + [, , ] + +The special attribute ``__members__`` is a dictionary mapping names to members. +It includes all names defined in the enumeration, including the aliases:: + + >>> for name, member in sorted(Shape.__members__.items()): + ... name, member + ... + ('alias_for_square', ) + ('circle', ) + ('diamond', ) + ('square', ) + +The ``__members__`` attribute can be used for detailed programmatic access to +the enumeration members. For example, finding all the aliases:: + + >>> [n for n, mbr in Shape.__members__.items() if mbr.name != n] + ['alias_for_square'] + +Comparisons +----------- + +Enumeration members are compared by identity:: + + >>> Color.red is Color.red + True + >>> Color.red is Color.blue + False + >>> Color.red is not Color.blue + True + +Ordered comparisons between enumeration values are *not* supported. Enum +members are not integers (but see `IntEnum`_ below):: + + >>> Color.red < Color.blue + Traceback (most recent call last): + File "", line 1, in + TypeError: unorderable types: Color() < Color() + +.. warning:: + + In Python 2 *everything* is ordered, even though the ordering may not + make sense. If you want your enumerations to have a sensible ordering + consider using an `OrderedEnum`_. + + +Equality comparisons are defined though:: + + >>> Color.blue == Color.red + False + >>> Color.blue != Color.red + True + >>> Color.blue == Color.blue + True + +Comparisons against non-enumeration values will always compare not equal +(again, ``IntEnum`` was explicitly designed to behave differently, see +below):: + + >>> Color.blue == 2 + False + + +Allowed members and attributes of enumerations +---------------------------------------------- + +The examples above use integers for enumeration values. Using integers is +short and handy (and provided by default by the `Enum Functional API`_), but not +strictly enforced. In the vast majority of use-cases, one doesn't care what +the actual value of an enumeration is. But if the value *is* important, +enumerations can have arbitrary values. + +Enumerations are Python classes, and can have methods and special methods as +usual. If we have this enumeration:: + + >>> class Mood(Enum): + ... funky = 1 + ... happy = 3 + ... + ... def describe(self): + ... # self is the member here + ... return self.name, self.value + ... + ... def __str__(self): + ... return 'my custom str! {0}'.format(self.value) + ... + ... @classmethod + ... def favorite_mood(cls): + ... # cls here is the enumeration + ... return cls.happy + +Then:: + + >>> Mood.favorite_mood() + + >>> Mood.happy.describe() + ('happy', 3) + >>> str(Mood.funky) + 'my custom str! 1' + +The rules for what is allowed are as follows: _sunder_ names (starting and +ending with a single underscore) are reserved by enum and cannot be used; +all other attributes defined within an enumeration will become members of this +enumeration, with the exception of *__dunder__* names and descriptors (methods +are also descriptors). + +.. note:: + + If your enumeration defines ``__new__`` and/or ``__init__`` then + whatever value(s) were given to the enum member will be passed into + those methods. See `Planet`_ for an example. + + +Restricted Enum subclassing +--------------------------- + +A new `Enum` class must have one base Enum class, up to one concrete +data type, and as many `object`-based mixin classes as needed. The +order of these base classes is:: + + def EnumName([mix-in, ...,] [data-type,] base-enum): + pass + +Also, subclassing an enumeration is allowed only if the enumeration does not define + +any members. So this is forbidden:: + + >>> class MoreColor(Color): + ... pink = 17 + Traceback (most recent call last): + ... + TypeError: cannot extend + +But this is allowed:: + + >>> class Foo(Enum): + ... def some_behavior(self): + ... pass + ... + >>> class Bar(Foo): + ... happy = 1 + ... sad = 2 + ... + +Allowing subclassing of enums that define members would lead to a violation of +some important invariants of types and instances. On the other hand, it makes +sense to allow sharing some common behavior between a group of enumerations. +(See `OrderedEnum`_ for an example.) + + +Pickling +-------- + +Enumerations can be pickled and unpickled:: + + >>> from aenum.test import Fruit + >>> from pickle import dumps, loads + >>> Fruit.tomato is loads(dumps(Fruit.tomato, 2)) + True + +The usual restrictions for pickling apply: picklable enums must be defined in +the top level of a module, since unpickling requires them to be importable +from that module. + +.. note:: + + With pickle protocol version 4 (introduced in Python 3.4) it is possible + to easily pickle enums nested in other classes. + + + +Enum Functional API +------------------- + +The ``Enum`` class is callable, providing the following functional API:: + + >>> Animal = Enum('Animal', 'ant bee cat dog') + >>> Animal + + >>> Animal.ant + + >>> Animal.ant.value + 1 + >>> list(Animal) + [, , , ] + +The semantics of this API resemble ``namedtuple``. The first argument +of the call to ``Enum`` is the name of the enumeration. + +The second argument is the *source* of enumeration member names. It can be a +whitespace-separated string of names, a sequence of names, a sequence of +2-tuples with key/value pairs, or a mapping (e.g. dictionary) of names to +values. The last two options enable assigning arbitrary values to +enumerations; the others auto-assign increasing integers starting with 1. A +new class derived from ``Enum`` is returned. In other words, the above +assignment to ``Animal`` is equivalent to:: + + >>> class Animals(Enum): + ... ant = 1 + ... bee = 2 + ... cat = 3 + ... dog = 4 + +Pickling enums created with the functional API can be tricky as frame stack +implementation details are used to try and figure out which module the +enumeration is being created in (e.g. it will fail if you use a utility +function in separate module, and also may not work on IronPython or Jython). +The solution is to specify the module name explicitly as follows:: + + >>> Animals = Enum('Animals', 'ant bee cat dog', module=__name__) + +Derived Enumerations +-------------------- + +IntEnum +^^^^^^^ + +A variation of ``Enum`` is provided which is also a subclass of +``int``. Members of an ``IntEnum`` can be compared to integers; +by extension, integer enumerations of different types can also be compared +to each other:: + + >>> from aenum import IntEnum + >>> class Shape(IntEnum): + ... circle = 1 + ... square = 2 + ... + >>> class Request(IntEnum): + ... post = 1 + ... get = 2 + ... + >>> Shape == 1 + False + >>> Shape.circle == 1 + True + >>> Shape.circle == Request.post + True + +However, they still can't be compared to standard ``Enum`` enumerations:: + + >>> class Shape(IntEnum): + ... circle = 1 + ... square = 2 + ... + >>> class Color(Enum): + ... red = 1 + ... green = 2 + ... + >>> Shape.circle == Color.red + False + +``IntEnum`` values behave like integers in other ways you'd expect:: + + >>> int(Shape.circle) + 1 + >>> ['a', 'b', 'c'][Shape.circle] + 'b' + >>> [i for i in range(Shape.square)] + [0, 1] + +For the vast majority of code, ``Enum`` is strongly recommended, +since ``IntEnum`` breaks some semantic promises of an enumeration (by +being comparable to integers, and thus by transitivity to other +unrelated enumerations). It should be used only in special cases where +there's no other choice; for example, when integer constants are +replaced with enumerations and backwards compatibility is required with code +that still expects integers. + + +IntFlag +^^^^^^^ + +The next variation of ``Enum`` provided, ``IntFlag``, is also based +on ``int``. The difference being ``IntFlag`` members can be combined +using the bitwise operators (&, \|, ^, ~) and the result is still an +``IntFlag`` member. However, as the name implies, ``IntFlag`` +members also subclass ``int`` and can be used wherever an ``int`` is +used. Any operation on an ``IntFlag`` member besides the bit-wise +operations will lose the ``IntFlag`` membership. + +Sample ``IntFlag`` class:: + + >>> from aenum import IntFlag + >>> class Perm(IntFlag): + ... _order_ = 'R W X' + ... R = 4 + ... W = 2 + ... X = 1 + ... + >>> Perm.R | Perm.W + + >>> Perm.R + Perm.W + 6 + >>> RW = Perm.R | Perm.W + >>> Perm.R in RW + True + +It is also possible to name the combinations:: + + >>> class Perm(IntFlag): + ... _order_ = 'R W X' + ... R = 4 + ... W = 2 + ... X = 1 + ... RWX = 7 + >>> Perm.RWX + + >>> ~Perm.RWX + + +Another important difference between ``IntFlag`` and ``Enum`` is that +if no flags are set (the value is 0), its boolean evaluation is ``False``:: + + >>> Perm.R & Perm.X + + >>> bool(Perm.R & Perm.X) + False + +Because ``IntFlag`` members are also subclasses of ``int`` they can +be combined with them:: + + >>> Perm.X | 4 + + +If the result is not a ``Flag`` then, depending on the ``_boundary_`` setting, +an exception is raised (``STRICT``), the extra bits are lost (``CONFORM``), or +it reverts to an int (``EJECT``): + + >>> from aenum import STRICT, CONFORM, EJECT + >>> Perm._boundary_ = STRICT + >>> Perm.X | 8 + Traceback (most recent call last): + ... + ValueError: Perm: invalid value: 9 + given 0b0 1001 + allowed 0b0 0111 + + >>> Perm._boundary_ = EJECT + >>> Perm.X | 8 + 9 + + >>> Perm._boundary_ = CONFORM + >>> Perm.X | 8 + + + +Flag +^^^^ + +The last variation is ``Flag``. Like ``IntFlag``, ``Flag`` +members can be combined using the bitwise operators (&, \|, ^, ~). Unlike +``IntFlag``, they cannot be combined with, nor compared against, any +other ``Flag`` enumeration, nor ``int``. While it is possible to +specify the values directly it is recommended to use ``auto`` as the +value and let ``Flag`` select an appropriate value. + +Like ``IntFlag``, if a combination of ``Flag`` members results in no +flags being set, the boolean evaluation is ``False``:: + + >>> from aenum import Flag, auto + >>> class Color(Flag): + ... RED = auto() + ... BLUE = auto() + ... GREEN = auto() + ... + >>> Color.RED & Color.GREEN + + >>> bool(Color.RED & Color.GREEN) + False + +Individual flags should have values that are powers of two (1, 2, 4, 8, ...), +while combinations of flags won't:: + + --> class Color(Flag): + ... RED = auto() + ... BLUE = auto() + ... GREEN = auto() + ... WHITE = RED | BLUE | GREEN + ... + --> Color.WHITE + + +Giving a name to the "no flags set" condition does not change its boolean +value:: + + >>> class Color(Flag): + ... BLACK = 0 + ... RED = auto() + ... BLUE = auto() + ... GREEN = auto() + ... + >>> Color.BLACK + + >>> bool(Color.BLACK) + False + +Flags can be iterated over to retrieve the individual truthy flags in the value:: + + >>> class Color(Flag): + ... _order_ = 'BLACK RED BLUE GREEN WHITE' + ... BLACK = 0 + ... RED = auto() + ... BLUE = auto() + ... GREEN = auto() + ... WHITE = RED | BLUE | GREEN + ... + >>> list(Color.GREEN) + [] + >>> list(Color.WHITE) + [, , ] + +.. note:: + + For the majority of new code, ``Enum`` and ``Flag`` are strongly + recommended, since ``IntEnum`` and ``IntFlag`` break some + semantic promises of an enumeration (by being comparable to integers, and + thus by transitivity to other unrelated enumerations). ``IntEnum`` + and ``IntFlag`` should be used only in cases where ``Enum`` and + ``Flag`` will not do; for example, when integer constants are replaced + with enumerations, or for interoperability with other systems. + + +Others +^^^^^^ + +While ``IntEnum`` is part of the ``aenum`` module, it would be very +simple to implement independently:: + + class MyIntEnum(int, Enum): + pass + +This demonstrates how similar derived enumerations can be defined; for example +a ``MyStrEnum`` that mixes in ``str`` instead of ``int``. + +Some rules: + +1. When subclassing ``Enum``, mix-in types must appear before + ``Enum`` itself in the sequence of bases, as in the ``MyIntEnum`` + example above. +2. While ``Enum`` can have members of any type, once you mix in an + additional type, all the members must have values of that type or be + convertible into that type. This restriction does not apply to mix-ins + which only add methods and don't specify another data type. +3. When another data type is mixed in, the ``value`` attribute is *not the + same* as the enum member itself, although it is equivalant and will compare + equal. +4. %-style formatting: ``%s`` and ``%r`` call ``Enum``'s ``__str__`` and + ``__repr__`` respectively; other codes (such as ``%i`` or ``%h`` for + MyIntEnum) treat the enum member as its mixed-in type. +5. ``str.__format__`` (or ``format``) will use the mixed-in + type's ``__format__``. If the ``Enum``'s ``str`` or ``repr`` is desired + use the ``!s`` or ``!r`` ``str`` format codes. + +.. note:: + + If you override the ``__str__`` method, then it will be used to provide the + string portion of the ``format()`` call. + +.. note:: + + Prior to Python 3.4 there is a bug in ``str``'s %-formatting: ``int`` + subclasses are printed as strings and not numbers when the ``%d``, ``%i``, + or ``%u`` codes are used. + + +Extra Goodies +------------- + +aenum supports a few extra techniques not found in the stdlib version. + +enum +^^^^ + +If you have several items to initialize your ``Enum`` members with and +would like to use keyword arguments, the ``enum`` helper is for you:: + + >>> from aenum import enum + >>> class Presidents(Enum): + ... Washington = enum('George Washington', circa=1776, death=1797) + ... Jackson = enum('Andrew Jackson', circa=1830, death=1837) + ... Lincoln = enum('Abraham Lincoln', circa=1860, death=1865) + ... + >>> Presidents.Lincoln + + +extend_enum +^^^^^^^^^^^ + +For those rare cases when you need to create your ``Enum`` in pieces, you +can use ``extend_enum`` to add new members after the initial creation +(the new member is returned):: + + >>> from aenum import extend_enum + >>> class Color(Enum): + ... red = 1 + ... green = 2 + ... blue = 3 + ... + >>> list(Color) + [, , ] + >>> extend_enum(Color, 'opacity', 4) + + >>> list(Color) + [, , , ] + >>> Color.opacity in Color + True + >>> Color.opacity.name == 'opacity' + True + >>> Color.opacity.value == 4 + True + >>> Color(4) + + >>> Color['opacity'] + + + --> Color.__members__ + OrderedDict([ + ('red', ), + ('green', ), + ('blue', ), + ('opacity', ) + ]) + +constant +^^^^^^^^ + +If you need to have some constant value in your ``Enum`` that isn't a member, +use ``constant``:: + + >>> from aenum import constant + >>> class Planet(Enum): + ... MERCURY = (3.303e+23, 2.4397e6) + ... EARTH = (5.976e+24, 6.37814e6) + ... JUPITER = (1.9e+27, 7.1492e7) + ... URANUS = (8.686e+25, 2.5559e7) + ... G = constant(6.67300E-11) + ... def __init__(self, mass, radius): + ... self.mass = mass # in kilograms + ... self.radius = radius # in meters + ... @property + ... def surface_gravity(self): + ... # universal gravitational constant (m3 kg-1 s-2) + ... return self.G * self.mass / (self.radius * self.radius) + ... + >>> Planet.EARTH.value + (5.976e+24, 6378140.0) + >>> Planet.EARTH.surface_gravity + 9.802652743337129 + >>> Planet.G + 6.673e-11 + >>> Planet.G = 9 + Traceback (most recent call last): + ... + AttributeError: Planet: cannot rebind constant 'G' + +skip +^^^^ + +If you need a standard attribute that is not converted into an ``Enum`` +member, use ``skip``:: + + >>> from aenum import skip + >>> class Color(Enum): + ... red = 1 + ... green = 2 + ... blue = 3 + ... opacity = skip(0.45) + ... + >>> Color.opacity + 0.45 + >>> Color.opacity = 0.77 + >>> Color.opacity + 0.77 + +start +^^^^^ + +``start`` can be used to turn on auto-numbering (useful for when you don't +care which numbers are assigned as long as they are consistent and in order) +The Python 3 version can look like this:: + + >>> class Color(Enum, start=1): # doctest: +SKIP + ... red, green, blue + ... + >>> Color.blue + + +This can also be done in Python 2, albeit not as elegantly (this also works in +Python 3):: + + >>> class Color(Enum): # doctest: +SKIP + ... _start_ = 1 + ... red = auto() + ... green = auto() + ... blue = auto() + ... + >>> Color.blue + + +init +^^^^ + +If you need an ``__init__`` method that does nothing besides save its +arguments, ``init`` is for you:: + + >>> class Planet(Enum, init='mass radius'): # doctest: +SKIP + ... MERCURY = (3.303e+23, 2.4397e6) + ... EARTH = (5.976e+24, 6.37814e6) + ... JUPITER = (1.9e+27, 7.1492e7) + ... URANUS = (8.686e+25, 2.5559e7) + ... G = constant(6.67300E-11) + ... @property + ... def surface_gravity(self): + ... # universal gravitational constant (m3 kg-1 s-2) + ... return self.G * self.mass / (self.radius * self.radius) + ... + >>> Planet.JUPITER.value + (1.9e+27, 71492000.0) + >>> Planet.JUPITER.mass + 1.9e+27 + +.. note:: + + Just as with ``start`` above, in Python 2 you must put the keyword as a + _sunder_ in the class body -- ``_init_ = 'mass radius'``. + +init and missing values +^^^^^^^^^^^^^^^^^^^^^^^ + +If ``_init_`` calls for values that are not supplied, ``_generate_next_value_`` +will be called in an effort to generate them. Here is an example in Python 2:: + + >>> from aenum import Enum + >>> class SelectionEnum(Enum): + ... _init_ = 'db user' + ... def __new__(cls, *args, **kwds): + ... count = len(cls.__members__) + ... obj = object.__new__(cls) + ... obj._count = count + ... obj._value_ = args + ... return obj + ... @staticmethod + ... def _generate_next_value_(name, start, count, values, *args, **kwds): + ... return (name, ) + args + ... + >>> class NotificationType(SelectionEnum): + ... # usually, name is the same as db + ... # but not for blanks + ... blank = '', '' + ... C = 'Catalog' + ... S = 'Sheet' + ... B = 'Both' + ... + >>> NotificationType.blank + + >>> NotificationType.B + + >>> NotificationType.B.db + 'B' + >>> NotificationType.B.user + 'Both' + +combining Flag with other data types +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Flag does support being combined with other data types. To support this you +need to provide a ``_create_pseudo_member_values_`` method which will be called +with the members in a composite flag. You may also need to provide a custom +``__new__`` method:: + + >>> class AnsiFlag(str, Flag): + ... def __new__(cls, value, code): + ... str_value = '\x1b[%sm' % code + ... obj = str.__new__(cls, str_value) + ... obj._value_ = value + ... obj.code = code + ... return obj + ... @classmethod + ... def _create_pseudo_member_values_(cls, members, *values): + ... code = ';'.join(m.code for m in members) + ... return values + (code, ) + ... _order_ = 'FG_Red FG_Green BG_Magenta BG_White' + ... FG_Red = '31' # ESC [ 31 m # red + ... FG_Green = '32' # ESC [ 32 m # green + ... BG_Magenta = '45' # ESC [ 35 m # magenta + ... BG_White = '47' # ESC [ 37 m # white + ... + >>> color = AnsiFlag.BG_White | AnsiFlag.FG_Red + >>> repr(color) + '' + >>> str.__repr__(color) + "'\\x1b[31;47m'" + +.. note:: + + If you do not provide your own ``_create_pseudo_member_values_`` the flags + may still combine, but may be missing functionality. + + +Decorators +---------- + +unique +^^^^^^ + +A ``class`` decorator specifically for enumerations. It searches an +enumeration's ``__members__`` gathering any aliases it finds; if any are +found ``ValueError`` is raised with the details:: + + >>> @unique + ... class NoDupes(Enum): + ... first = 'one' + ... second = 'two' + ... third = 'two' + Traceback (most recent call last): + ... + ValueError: duplicate names found in : third -> second + + +Interesting examples +-------------------- + +While ``Enum`` and ``IntEnum`` are expected to cover the majority of +use-cases, they cannot cover them all. Here are recipes for some different +types of enumerations that can be used directly (the first three are included +in the module), or as examples for creating one's own. + + +AutoNumber +^^^^^^^^^^ + +Avoids having to specify the value for each enumeration member:: + + >>> class AutoNumber(Enum): + ... def __new__(cls): + ... value = len(cls.__members__) + 1 + ... obj = object.__new__(cls) + ... obj._value_ = value + ... return obj + ... + >>> class Color(AutoNumber): + ... _order_ = "red green blue" # only needed in 2.x + ... red = () + ... green = () + ... blue = () + ... + >>> Color.green.value == 2 + True + +.. note:: + + The `__new__` method, if defined, is used during creation of the Enum + members; it is then replaced by Enum's `__new__` which is used after + class creation for lookup of existing members. Due to the way Enums are + supposed to behave, there is no way to customize Enum's `__new__` without + modifying the class after it is created. + + +UniqueEnum +^^^^^^^^^^ + +Raises an error if a duplicate member name is found instead of creating an +alias:: + + >>> class UniqueEnum(Enum): + ... def __init__(self, *args): + ... cls = self.__class__ + ... if any(self.value == e.value for e in cls): + ... a = self.name + ... e = cls(self.value).name + ... raise ValueError( + ... "aliases not allowed in UniqueEnum: %r --> %r" + ... % (a, e)) + ... + >>> class Color(UniqueEnum): + ... _order_ = 'red green blue' + ... red = 1 + ... green = 2 + ... blue = 3 + ... grene = 2 + Traceback (most recent call last): + ... + ValueError: aliases not allowed in UniqueEnum: 'grene' --> 'green' + + +OrderedEnum +^^^^^^^^^^^ + +An ordered enumeration that is not based on ``IntEnum`` and so maintains +the normal ``Enum`` invariants (such as not being comparable to other +enumerations):: + + >>> class OrderedEnum(Enum): + ... def __ge__(self, other): + ... if self.__class__ is other.__class__: + ... return self._value_ >= other._value_ + ... return NotImplemented + ... def __gt__(self, other): + ... if self.__class__ is other.__class__: + ... return self._value_ > other._value_ + ... return NotImplemented + ... def __le__(self, other): + ... if self.__class__ is other.__class__: + ... return self._value_ <= other._value_ + ... return NotImplemented + ... def __lt__(self, other): + ... if self.__class__ is other.__class__: + ... return self._value_ < other._value_ + ... return NotImplemented + ... + >>> class Grade(OrderedEnum): + ... __ordered__ = 'A B C D F' + ... A = 5 + ... B = 4 + ... C = 3 + ... D = 2 + ... F = 1 + ... + >>> Grade.C < Grade.A + True + + +Planet +^^^^^^ + +If ``__new__`` or ``__init__`` is defined the value of the enum member +will be passed to those methods:: + + >>> class Planet(Enum): + ... MERCURY = (3.303e+23, 2.4397e6) + ... VENUS = (4.869e+24, 6.0518e6) + ... EARTH = (5.976e+24, 6.37814e6) + ... MARS = (6.421e+23, 3.3972e6) + ... JUPITER = (1.9e+27, 7.1492e7) + ... SATURN = (5.688e+26, 6.0268e7) + ... URANUS = (8.686e+25, 2.5559e7) + ... NEPTUNE = (1.024e+26, 2.4746e7) + ... def __init__(self, mass, radius): + ... self.mass = mass # in kilograms + ... self.radius = radius # in meters + ... @property + ... def surface_gravity(self): + ... # universal gravitational constant (m3 kg-1 s-2) + ... G = 6.67300E-11 + ... return G * self.mass / (self.radius * self.radius) + ... + >>> Planet.EARTH.value + (5.976e+24, 6378140.0) + >>> Planet.EARTH.surface_gravity + 9.802652743337129 + + +How are Enums different? +------------------------ + +Enums have a custom metaclass that affects many aspects of both derived Enum +classes and their instances (members). + + +Enum Classes +^^^^^^^^^^^^ + +The ``EnumMeta`` metaclass is responsible for providing the +``__contains__``, ``__dir__``, ``__iter__`` and other methods that +allow one to do things with an ``Enum`` class that fail on a typical +class, such as ``list(Color)`` or ``some_var in Color``. ``EnumMeta`` is +responsible for ensuring that various other methods on the final ``Enum`` +class are correct (such as ``__new__``, ``__getnewargs__``, +``__str__`` and ``__repr__``). + +.. note:: + + ``__dir__`` is not changed in the Python 2 line as it messes up some + of the decorators included in the stdlib. + + +Enum Members (aka instances) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The most interesting thing about Enum members is that they are singletons. +``EnumMeta`` creates them all while it is creating the ``Enum`` +class itself, and then puts a custom ``__new__`` in place to ensure +that no new ones are ever instantiated by returning only the existing +member instances. + + +Finer Points +^^^^^^^^^^^^ + +``Enum`` members are instances of an ``Enum`` class, but are not +accessible as `EnumClass.member1.member2`. +(changed in version 1.1.1 to be accessible) +(changed in version 2.2.4 to be inaccessible):: + + >>> class FieldTypes(Enum): + ... name = 1 + ... value = 2 + ... size = 3 + ... + >>> FieldTypes.size.value + 3 + >>> FieldTypes.size + + >>> FieldTypes.value.size + Traceback (most recent call last): + ... + AttributeError: member has no attribute 'size' + +The ``__members__`` attribute is only available on the class. + + +``__members__`` is always an ``OrderedDict``, with the order being the +definition order in Python 3.x or the order in ``_order_`` in Python 2.7; +if no ``_order_`` was specified in Python 2.7 then the order of +``__members__`` is either increasing value or alphabetically by name. + +If you give your ``Enum`` subclass extra methods, like the `Planet`_ +class above, those methods will show up in a `dir` of the member, +but not of the class (in Python 3.x):: + + --> dir(Planet) + ['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', + 'VENUS', '__class__', '__doc__', '__members__', '__module__'] + --> dir(Planet.EARTH) + ['__class__', '__doc__', '__module__', 'name', 'surface_gravity', 'value'] + +A ``__new__`` method will only be used for the creation of the +``Enum`` members -- after that it is replaced. This means if you wish to +change how ``Enum`` members are looked up you either have to write a +helper function or a ``classmethod``. + +.. note:: + + If you create your own ``__new__`` you should set the ``_value_`` in it; + if you do not, aenum will try to, but will raise a ``TypeError`` if it + cannot. + +If the stdlib ``enum`` is available (Python 3.4+ and it hasn't been shadowed +by, for example, ``enum34``) then aenum will be a subclass of it. + +To use the ``AddValue``, ``MultiValue``, ``NoAlias``, and ``Unique`` flags +in Py2 or Py2/Py3 codebases, use ``_settings_ = ...`` in the class body. + +To use ``init`` in Py2 or Py2/Py3 codebases use ``_init_`` in the class body. + +To use ``start`` in Py2 or Py2/Py3 codebases use ``_start_`` in the class body. + +When creating class bodies dynamically, put any variables you need to use into +``_ignore_``:: + + >>> from datetime import timedelta + >>> from aenum import NoAlias + >>> class Period(timedelta, Enum): + ... ''' + ... different lengths of time + ... ''' + ... _init_ = 'value period' + ... _settings_ = NoAlias + ... _ignore_ = 'Period i' + ... Period = vars() + ... for i in range(31): + ... Period['day_%d' % i] = i, 'day' + ... for i in range(15): + ... Period['week_%d' % i] = i*7, 'week' + ... + >>> hasattr(Period, '_ignore_') + False + >>> hasattr(Period, 'Period') + False + >>> hasattr(Period, 'i') + False + +The name listed in ``_ignore_``, as well as ``_ignore_`` itself, will not be +present in the final enumeration as neither attributes nor members. + +.. note:: + + except for __dunder__ attributes/methods, all _sunder_ attributes must + be before any thing else in the class body + +.. note:: + + all _sunder_ attributes that affect member creation are only looked up in + the last ``Enum`` class listed in the class header + + +Creating NamedTuples +-------------------- + +Simple +^^^^^^ + +The most common way to create a new NamedTuple will be via the functional API:: + + >>> from aenum import NamedTuple + >>> Book = NamedTuple('Book', 'title author genre', module=__name__) + +This creates a ``NamedTuple`` called ``Book`` that will always contain three +items, each of which is also addressable as ``title``, ``author``, or ``genre``. + +``Book`` instances can be created using positional or keyword argements or a +mixture of the two:: + + >>> b1 = Book('Lord of the Rings', 'J.R.R. Tolkien', 'fantasy') + >>> b2 = Book(title='Jhereg', author='Steven Brust', genre='fantasy') + >>> b3 = Book('Empire', 'Orson Scott Card', genre='scifi') + +If too few or too many arguments are used a ``TypeError`` will be raised:: + + >>> b4 = Book('Hidden Empire') + Traceback (most recent call last): + ... + TypeError: values not provided for field(s): author, genre + >>> b5 = Book(genre='business') + Traceback (most recent call last): + ... + TypeError: values not provided for field(s): title, author + +As a ``class`` the above ``Book`` ``NamedTuple`` would look like:: + + >>> class Book(NamedTuple): + ... title = 0 + ... author = 1 + ... genre = 2 + ... + +For compatibility with the stdlib ``namedtuple``, NamedTuple also has the +``_asdict``, ``_make``, and ``_replace`` methods, and the ``_fields`` +attribute, which all function similarly:: + + >>> class Point(NamedTuple): + ... x = 0, 'horizontal coordinate', 1 + ... y = 1, 'vertical coordinate', -1 + ... + >>> class Color(NamedTuple): + ... r = 0, 'red component', 11 + ... g = 1, 'green component', 29 + ... b = 2, 'blue component', 37 + ... + >>> Pixel = NamedTuple('Pixel', Point+Color, module=__name__) + >>> pixel = Pixel(99, -101, 255, 128, 0) + + >>> pixel._asdict() + OrderedDict([('x', 99), ('y', -101), ('r', 255), ('g', 128), ('b', 0)]) + + >>> Point._make((4, 5)) + Point(x=4, y=5) + + >>> purple = Color(127, 0, 127) + >>> mid_gray = purple._replace(g=127) + >>> mid_gray + Color(r=127, g=127, b=127) + + >>> pixel._fields + ['x', 'y', 'r', 'g', 'b'] + + >>> Pixel._fields + ['x', 'y', 'r', 'g', 'b'] + + +Advanced +^^^^^^^^ + +The simple method of creating ``NamedTuples`` requires always specifying all +possible arguments when creating instances; failure to do so will raise +exceptions:: + + >>> class Point(NamedTuple): + ... x = 0 + ... y = 1 + ... + >>> Point() + Traceback (most recent call last): + ... + TypeError: values not provided for field(s): x, y + >>> Point(1) + Traceback (most recent call last): + ... + TypeError: values not provided for field(s): y + >>> Point(y=2) + Traceback (most recent call last): + ... + TypeError: values not provided for field(s): x + +However, it is possible to specify both docstrings and default values when +creating a ``NamedTuple`` using the class method:: + + >>> class Point(NamedTuple): + ... x = 0, 'horizontal coordinate', 0 + ... y = 1, 'vertical coordinate', 0 + ... + >>> Point() + Point(x=0, y=0) + >>> Point(1) + Point(x=1, y=0) + >>> Point(y=2) + Point(x=0, y=2) + +It is also possible to create ``NamedTuples`` that only have named attributes +for certain fields; any fields without names can still be accessed by index:: + + >>> class Person(NamedTuple): + ... fullname = 2 + ... phone = 5 + ... + >>> p = Person('Ethan', 'Furman', 'Ethan Furman', + ... 'ethan at stoneleaf dot us', + ... 'ethan.furman', '999.555.1212') + >>> p + Person('Ethan', 'Furman', 'Ethan Furman', 'ethan at stoneleaf dot us', + 'ethan.furman', '999.555.1212') + >>> p.fullname + 'Ethan Furman' + >>> p.phone + '999.555.1212' + >>> p[0] + 'Ethan' + +In the above example the last named field was also the last field possible; in +those cases where you don't need to have the last possible field named, you can +provide a ``_size_`` of ``TupleSize.minimum`` to declare that more fields are +okay:: + + >>> from aenum import TupleSize + >>> class Person(NamedTuple): + ... _size_ = TupleSize.minimum + ... first = 0 + ... last = 1 + ... + +or, optionally if using Python 3:: + + >>> class Person(NamedTuple, size=TupleSize.minimum): # doctest: +SKIP + ... first = 0 + ... last = 1 + +and in use:: + + >>> Person('Ethan', 'Furman') + Person(first='Ethan', last='Furman') + + >>> Person('Ethan', 'Furman', 'ethan.furman') + Person('Ethan', 'Furman', 'ethan.furman') + + >>> Person('Ethan', 'Furman', 'ethan.furman', 'yay Python!') + Person('Ethan', 'Furman', 'ethan.furman', 'yay Python!') + + >>> Person('Ethan') + Traceback (most recent call last): + ... + TypeError: values not provided for field(s): last + +Also, for those cases where even named fields may not be present, you can +specify ``TupleSize.variable``:: + + >>> class Person(NamedTuple): + ... _size_ = TupleSize.variable + ... first = 0 + ... last = 1 + ... + + >>> Person('Ethan') + Person('Ethan') + + >>> Person(last='Furman') + Traceback (most recent call last): + ... + TypeError: values not provided for field(s): first + +Creating new ``NamedTuples`` from existing ``NamedTuples`` is simple:: + + >>> Point = NamedTuple('Point', 'x y') + >>> Color = NamedTuple('Color', 'r g b') + >>> Pixel = NamedTuple('Pixel', Point+Color, module=__name__) + >>> Pixel + + +The existing fields in the bases classes are renumbered to fit the new class, +but keep their doc strings and default values. If you use standard +subclassing:: + + >>> Point = NamedTuple('Point', 'x y') + >>> class Pixel(Point): + ... r = 2, 'red component', 11 + ... g = 3, 'green component', 29 + ... b = 4, 'blue component', 37 + ... + >>> Pixel.__fields__ + ['x', 'y', 'r', 'g', 'b'] + +You must manage the numbering yourself. + + +Creating NamedConstants +----------------------- + +A ``NamedConstant`` class is created much like an ``Enum``:: + + >>> from aenum import NamedConstant + >>> class Konstant(NamedConstant): + ... PI = 3.14159 + ... TAU = 2 * PI + + >>> Konstant.PI + + + >> print(Konstant.PI) + 3.14159 + + >>> Konstant.PI = 'apple' + Traceback (most recent call last): + ... + AttributeError: cannot rebind constant + + >>> del Konstant.PI + Traceback (most recent call last): + ... + AttributeError: cannot delete constant diff --git a/venv/Lib/site-packages/aenum/test.py b/venv/Lib/site-packages/aenum/test.py new file mode 100644 index 00000000..224293cb --- /dev/null +++ b/venv/Lib/site-packages/aenum/test.py @@ -0,0 +1,6832 @@ +# -*- coding: utf-8 -*- + +from __future__ import division, print_function +import sys +import aenum +import doctest +import os +import shutil +import tempfile +import textwrap +import unittest +import uuid +import warnings +from aenum import EnumType, EnumMeta, Enum, IntEnum, StrEnum, LowerStrEnum, UpperStrEnum +from aenum import AutoNumberEnum, MultiValueEnum, OrderedEnum, UniqueEnum, AddValueEnum, Flag, IntFlag +from aenum import NamedTuple, TupleSize, NamedConstant, constant, NoAlias, AddValue, Unique +from aenum import STRICT, CONFORM, EJECT, KEEP +from aenum import _reduce_ex_by_name, unique, skip, extend_enum, auto, enum, MultiValue, member, nonmember, no_arg +from aenum import basestring, baseinteger, unicode, enum_property +from aenum import pyver, PY2, PY3, PY2_6, PY3_3, PY3_4, PY3_5, PY3_6, PY3_11 +from collections import OrderedDict +from datetime import timedelta +from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL +from operator import or_ as _or_, and_ as _and_, xor as _xor_, inv as _inv_ +from operator import abs as _abs_, add as _add_, floordiv as _floordiv_ +from operator import lshift as _lshift_, rshift as _rshift_, mod as _mod_ +from operator import mul as _mul_, neg as _neg_, pos as _pos_, pow as _pow_ +from operator import truediv as _truediv_, sub as _sub_ +if PY2: + from operator import div as _div_ +try: + import threading +except ImportError: + threading = None + +try: + any +except NameError: + from aenum import any + +MODULE = __name__ +SHORT_MODULE = MODULE.split('.')[-1] + +def load_tests(loader, tests, ignore): + tests.addTests(doctest.DocTestSuite(aenum)) + tests.addTests(doctest.DocFileSuite( + 'doc/aenum.rst', + package=aenum, + optionflags=doctest.ELLIPSIS|doctest.NORMALIZE_WHITESPACE, + )) + return tests + +class TestCase(unittest.TestCase): + + def __init__(self, *args, **kwds): + regex = getattr(self, 'assertRaisesRegex', None) + if regex is None: + self.assertRaisesRegex = getattr(self, 'assertRaisesRegexp') + super(TestCase, self).__init__(*args, **kwds) + + +# for pickle tests +try: + class Stooges(Enum): + LARRY = 1 + CURLY = 2 + MOE = 3 +except Exception: + Stooges = sys.exc_info()[1] + +try: + class IntStooges(int, Enum): + LARRY = 1 + CURLY = 2 + MOE = 3 +except Exception: + IntStooges = sys.exc_info()[1] + +try: + class FloatStooges(float, Enum): + LARRY = 1.39 + CURLY = 2.72 + MOE = 3.142596 +except Exception: + FloatStooges = sys.exc_info()[1] + +try: + class FlagStooges(Flag): + LARRY = 1 + CURLY = 2 + MOE = 3 +except Exception as exc: + FlagStooges = exc + +try: + LifeForm = NamedTuple('LifeForm', 'branch genus species', module=__name__) +except Exception: + LifeForm = sys.exc_info()[1] + +try: + class DeathForm(NamedTuple): + color = 0 + rigidity = 1 + odor = 2 +except Exception: + DeathForm = sys.exc_info()[1] + +# for pickle test and subclass tests +try: + class Name(StrEnum): + BDFL = 'Guido van Rossum' + FLUFL = 'Barry Warsaw' +except Exception: + Name = sys.exc_info()[1] + +try: + Question = Enum('Question', 'who what when where why', module=__name__) +except Exception: + Question = sys.exc_info()[1] + +try: + Answer = Enum('Answer', 'him this then there because') +except Exception: + Answer = sys.exc_info()[1] + +try: + class WhatsIt(NamedTuple): + def what(self): + return self[0] + class ThatsIt(WhatsIt): + blah = 0 + bleh = 1 +except Exception: + ThatsIt = sys.exc_info()[1] + +# for doctests +try: + class Fruit(Enum): + tomato = 1 + banana = 2 + cherry = 3 +except Exception: + pass + +def test_pickle_dump_load(assertion, source, target=None, protocol=(0, HIGHEST_PROTOCOL)): + start, stop = protocol + failures = [] + for protocol in range(start, stop+1): + try: + if target is None: + assertion(loads(dumps(source, protocol=protocol)), source) + else: + assertion(loads(dumps(source, protocol=protocol)), target) + except Exception: + exc, tb = sys.exc_info()[1:] + failures.append('%2d: %s' %(protocol, exc)) + if failures: + raise ValueError('Failed with protocols: %s' % ', '.join(failures)) + +def test_pickle_exception(assertion, exception, obj, + protocol=(0, HIGHEST_PROTOCOL)): + start, stop = protocol + failures = [] + for protocol in range(start, stop+1): + try: + assertion(exception, dumps, obj, protocol=protocol) + except Exception: + exc = sys.exc_info()[1] + failures.append('%d: %s %s' % (protocol, exc.__class__.__name__, exc)) + if failures: + raise ValueError('Failed with protocols: %s' % ', '.join(failures)) + +if PY3: + from aenum.test_v3 import TestEnumV3, TestOrderV3, TestNamedTupleV3, TestStackoverflowAnswersV3, TestIssuesV3, TestExtendEnumV3 + from aenum import test_v3 + test_v3.IntStooges = IntStooges + test_v3.test_pickle_exception = test_pickle_exception + test_v3.test_pickle_dump_load = test_pickle_dump_load + +# for subclassing tests + +class classproperty(object): + + def __init__(self, fget=None, fset=None, fdel=None, doc=None): + self.fget = fget + self.fset = fset + self.fdel = fdel + if doc is None and fget is not None: + doc = fget.__doc__ + self.__doc__ = doc + + def __get__(self, instance, ownerclass): + return self.fget(ownerclass) + + +# tests +class TestOrder(TestCase): + """ + Test _order_ extra/missing members. + """ + + def test_same_members(self): + class Color(Enum): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 3 + + def test_same_members_with_aliases(self): + class Color(Enum): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 3 + verde = green + + def test_order_has_extra_members(self): + with self.assertRaisesRegex(TypeError, r'member order does not match _order_'): + class Color(Enum): + _order_ = 'red green blue purple' + red = 1 + green = 2 + blue = 3 + + def test_order_has_extra_members_with_aliases(self): + with self.assertRaisesRegex(TypeError, r'member order does not match _order_'): + class Color(Enum): + _order_ = 'red green blue purple' + red = 1 + green = 2 + blue = 3 + verde = green + + def test_enum_has_extra_members(self): + with self.assertRaisesRegex(TypeError, r'member order does not match _order_'): + class Color(Enum): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 3 + purple = 4 + + def test_enum_has_extra_members_with_aliases(self): + with self.assertRaisesRegex(TypeError, r'member order does not match _order_'): + class Color(Enum): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 3 + purple = 4 + verde = green + + def test_same_members_flag(self): + class Color(Flag): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 4 + + def test_same_members_with_aliases_flag(self): + class Color(Flag): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 4 + verde = green + + def test_order_has_extra_members_flag(self): + with self.assertRaisesRegex(TypeError, r'member order does not match _order_'): + class Color(Flag): + _order_ = 'red green blue purple' + red = 1 + green = 2 + blue = 4 + + def test_order_has_extra_members_with_aliases_flag(self): + with self.assertRaisesRegex(TypeError, r'member order does not match _order_'): + class Color(Flag): + _order_ = 'red green blue purple' + red = 1 + green = 2 + blue = 4 + verde = green + + def test_enum_has_extra_members_flag(self): + with self.assertRaisesRegex(TypeError, r'member order does not match _order_'): + class Color(Flag): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 4 + purple = 8 + + def test_enum_has_extra_members_with_aliases_flag(self): + with self.assertRaisesRegex(TypeError, r'member order does not match _order_'): + class Color(Flag): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 4 + purple = 8 + verde = green + + +class TestAutoValue(TestCase): + + def test_bare(self): + # + class BareEnum(Enum): + _order_ = 'ONE TWO THREE' + ONE = auto() + TWO = auto() + THREE = auto() + self.assertEqual(BareEnum.THREE.value, 3) + # + class BareIntEnum(IntEnum): + _order_ = 'ONE TWO THREE' + ONE = auto() + TWO = auto() + THREE = auto() + self.assertEqual(BareIntEnum.THREE, 3) + # + class BareFlag(Flag): + _order_ = 'ONE TWO THREE' + ONE = auto() + TWO = auto() + THREE = auto() + self.assertEqual(BareFlag.THREE.value, 4) + # + class BareIntFlag(IntFlag): + _order_ = 'ONE TWO THREE' + ONE = auto() + TWO = auto() + THREE = auto() + self.assertEqual(BareIntFlag.THREE, 4) + + def test_init_only_final(self): + # + class InitEnumValue(Enum): + _init_ = 'value description' + _order_ = 'ONE TWO THREE' + ONE = 'the loneliest number' + TWO = 'the number with you' + THREE = 'a triangle' + self.assertEqual(InitEnumValue.THREE.value, 3) + self.assertEqual(InitEnumValue.THREE.description, 'a triangle') + # + class InitEnum(Enum): + _init_ = 'value description' + _order_ = 'ONE TWO THREE' + ONE = 'the loneliest number' + TWO = 'the number with you' + THREE = 'a triangle' + self.assertEqual(InitEnum.THREE.value, 3) + self.assertEqual(InitEnum.THREE.description, 'a triangle') + # + class InitIntEnum(IntEnum): + _init_ = 'value description' + _order_ = 'ONE TWO THREE' + ONE = 'the loneliest number' + TWO = 'the number with you' + THREE = 'a triangle' + self.assertEqual(InitIntEnum.THREE, 3) + self.assertEqual(InitIntEnum.THREE.description, 'a triangle') + # + class InitFlag(Flag): + _init_ = 'value description' + _order_ = 'ONE TWO THREE' + ONE = 'the loneliest number' + TWO = 'the number with you' + THREE = 'a triangle' + self.assertEqual(InitFlag.THREE.value, 4) + self.assertEqual(InitFlag.THREE.description, 'a triangle') + # + class InitIntFlag(IntFlag): + _init_ = 'value description' + _order_ = 'ONE TWO THREE' + ONE = 'the loneliest number' + TWO = 'the number with you' + THREE = 'a triangle' + self.assertEqual(InitIntFlag.THREE, 4) + self.assertEqual(InitIntFlag.THREE.description, 'a triangle') + + def test_init_only_inherit(self): + # + class InitInheritEnum(Enum): + _init_ = 'value description' + # + class InitEnum(InitInheritEnum): + _order_ = 'ONE TWO THREE' + ONE = 'the loneliest number' + TWO = 'the number with you' + THREE = 'a triangle' + self.assertEqual(InitEnum.THREE.value, 3) + self.assertEqual(InitEnum.THREE.description, 'a triangle') + # + # + class InitInheritValueEnum(Enum): + _init_ = 'value description' + # + class InitEnum(InitInheritValueEnum): + _order_ = 'ONE TWO THREE' + ONE = 'the loneliest number' + TWO = 'the number with you' + THREE = 'a triangle' + self.assertEqual(InitEnum.THREE.value, 3) + self.assertEqual(InitEnum.THREE.description, 'a triangle') + # + class InitIntEnum(int, InitInheritValueEnum): + _order_ = 'ONE TWO THREE' + ONE = 'the loneliest number' + TWO = 'the number with you' + THREE = 'a triangle' + self.assertEqual(InitIntEnum.THREE, 3) + self.assertEqual(InitIntEnum.THREE.description, 'a triangle') + # + class InitInheritValueFlag(Flag): + _init_ = 'value description' + # + class InitFlag(InitInheritValueFlag): + _order_ = 'ONE TWO THREE' + ONE = 'the loneliest number' + TWO = 'the number with you' + THREE = 'a triangle' + self.assertEqual(InitFlag.THREE.value, 4) + self.assertEqual(InitFlag.THREE.description, 'a triangle') + # + class InitIntFlag(int, InitInheritValueFlag): + _order_ = 'ONE TWO THREE' + ONE = 'the loneliest number' + TWO = 'the number with you' + THREE = 'a triangle' + self.assertEqual(InitIntFlag.THREE, 4) + self.assertEqual(InitIntFlag.THREE.description, 'a triangle') + + def test_new_only_final(self): + # + class NewFinalEnum(Enum): + _order_ = 'ONE TWO THREE' + def __new__(cls, value): + member = object.__new__(cls) + member._value_ = value + member.proof = 'NFE1' + return member + ONE = auto() + TWO = auto() + THREE = auto() + self.assertEqual(NewFinalEnum.THREE.value, 3) + self.assertEqual(NewFinalEnum.TWO.proof, 'NFE1') + # + class NewFinalIntEnum(IntEnum): + _order_ = 'ONE TWO THREE' + def __new__(cls, value): + member = int.__new__(cls, value) + member._value_ = value + member.proof = 'NFE2' + return member + ONE = auto() + TWO = auto() + THREE = auto() + self.assertEqual(NewFinalIntEnum.THREE, 3) + self.assertEqual(NewFinalIntEnum.TWO.proof, 'NFE2') + # + class NewFinalFlag(Flag): + _order_ = 'ONE TWO THREE' + def __new__(cls, value): + member = object.__new__(cls) + member._value_ = value + member.proof = 'NFE3' + return member + ONE = auto() + TWO = auto() + THREE = auto() + self.assertEqual(NewFinalFlag.THREE.value, 4) + self.assertEqual(NewFinalFlag.TWO.proof, 'NFE3') + # + class NewFinalIntFlag(IntFlag): + _order_ = 'ONE TWO THREE' + def __new__(cls, value): + member = int.__new__(cls, value) + member._value_ = value + member.proof = 'NFE4' + return member + ONE = auto() + TWO = auto() + THREE = auto() + self.assertEqual(NewFinalIntFlag.THREE, 4) + self.assertEqual(NewFinalIntFlag.TWO.proof, 'NFE4') + # + class NewFinalStrEnum(str, Enum): + # + _order_ = "AllReset Bright FG_Cyan BG_Black" + # + def __new__(cls, value, code, description): + str_value = '\x1b[%sm' % code + obj = str.__new__(cls, str_value) + obj._value_ = value + obj.code = code + obj.description = description + return obj + # + __str__ = str.__str__ + # + AllReset = '0', 'reset all (colors and brightness)' + Bright = '1', 'bright lights!' + FG_Cyan = '36', 'cyan' + BG_Black = '40', 'black' + self.assertEqual(NewFinalStrEnum.FG_Cyan.value, 3) + self.assertEqual(NewFinalStrEnum.BG_Black.value, 4) + self.assertEqual(NewFinalStrEnum.AllReset.code, '0') + self.assertEqual(NewFinalStrEnum.Bright.description, 'bright lights!') + # + class NewFinalStrFlag(str, Flag): + # + _order_ = "AllReset Bright FG_Cyan BG_Black" + # + def __new__(cls, value, code, description): + str_value = '\x1b[%sm' % code + obj = str.__new__(cls, str_value) + obj._value_ = value + obj.code = code + obj.description = description + return obj + # + __str__ = str.__str__ + # + AllReset = '0', 'reset all (colors and brightness)' + Bright = '1', 'bright lights!' + FG_Cyan = '36', 'cyan' + BG_Black = '40', 'black' + self.assertEqual(NewFinalStrFlag.FG_Cyan.value, 4) + self.assertEqual(NewFinalStrFlag.BG_Black.value, 8) + self.assertEqual(NewFinalStrFlag.AllReset.code, '0') + self.assertEqual(NewFinalStrFlag.Bright.description, 'bright lights!') + + def test_new_only_inherited(self): + # + class NewInheritEnum(Enum): + def __new__(cls, value): + if cls._member_type_ is int: + member = int.__new__(cls, value*2) + else: + member = object.__new__(cls) + member._value_ = value * 2 + member.proof = 'NIE' + return member + # + class NewFinalEnum(NewInheritEnum): + _order_ = 'ONE TWO THREE' + ONE = auto() + TWO = auto() + THREE = auto() + self.assertEqual(NewFinalEnum.THREE.value, 6) + self.assertEqual(NewFinalEnum.TWO.proof, 'NIE') + # + class NewFinalIntEnum(int, NewInheritEnum): + _order_ = 'ONE TWO THREE' + ONE = auto() + TWO = auto() + THREE = auto() + self.assertEqual(NewFinalIntEnum.THREE, 6) + self.assertEqual(NewFinalIntEnum.TWO.proof, 'NIE') + # + class NewInheritFlag(Flag): + def __new__(cls, value): + if cls._member_type_ is int: + member = int.__new__(cls, value*2) + else: + member = object.__new__(cls) + member._value_ = value * 2 + member.proof = 'NIE' + return member + # + class NewFinalFlag(NewInheritFlag): + _order_ = 'ONE TWO THREE' + ONE = auto() + TWO = auto() + THREE = auto() + self.assertEqual(NewFinalFlag.THREE.value, 8) + self.assertEqual(NewFinalFlag.TWO.proof, 'NIE') + # + class NewFinalIntFlag(int, NewInheritFlag): + _order_ = 'ONE TWO THREE' + ONE = auto() + TWO = auto() + THREE = auto() + self.assertEqual(NewFinalIntFlag.THREE, 8) + self.assertEqual(NewFinalIntFlag.TWO.proof, 'NIE') + + def test_init_new_only(self): + # + class InitNewEnum(Enum): + _init_ = "value description" + _order_ = 'ONE TWO THREE' + def __new__(cls, value, *args): + member = object.__new__(cls) + member._value_ = value + member.proof = 'INE1' + return member + ONE = 'the loneliest number' + TWO = 'the number with you' + THREE = 'a triangle' + self.assertEqual(InitNewEnum.THREE.value, 3) + self.assertEqual(InitNewEnum.THREE.description, 'a triangle') + self.assertEqual(InitNewEnum.TWO.proof, 'INE1') + # + class InitNewIntEnum(IntEnum): + _init_ = "value description" + _order_ = 'ONE TWO THREE' + def __new__(cls, value, *args): + member = int.__new__(cls, value) + member._value_ = value + member.proof = 'INE2' + return member + ONE = 'the loneliest number' + TWO = 'the number with you' + THREE = 'a triangle' + self.assertEqual(InitNewIntEnum.THREE, 3) + self.assertEqual(InitNewIntEnum.THREE.description, 'a triangle') + self.assertEqual(InitNewIntEnum.TWO.proof, 'INE2') + # + class InitNewFlag(Flag): + _init_ = "value description" + _order_ = 'ONE TWO THREE' + def __new__(cls, value, *args): + member = object.__new__(cls) + member._value_ = value + member.proof = 'INE3' + return member + ONE = 'the loneliest number' + TWO = 'the number with you' + THREE = 'a triangle' + self.assertEqual(InitNewFlag.THREE.value, 4) + self.assertEqual(InitNewFlag.THREE.description, 'a triangle') + self.assertEqual(InitNewFlag.TWO.proof, 'INE3') + # + class InitNewIntFlag(IntFlag): + _init_ = "value description" + _order_ = 'ONE TWO THREE' + def __new__(cls, value, *args): + member = int.__new__(cls, value) + member._value_ = value + member.proof = 'INE4' + return member + ONE = 'the loneliest number' + TWO = 'the number with you' + THREE = 'a triangle' + self.assertEqual(InitNewIntFlag.THREE, 4) + self.assertEqual(InitNewIntFlag.THREE.description, 'a triangle') + self.assertEqual(InitNewIntFlag.TWO.proof, 'INE4') + + def test_init_new_inherit(self): + # + class InitNew(Enum): + _init_ = "value description" + def __new__(cls, value, *args): + member = object.__new__(cls) + member._value_ = value + member.proof = 'IN' + return member + # + class InitNewEnum(InitNew): + _order_ = 'ONE TWO THREE' + ONE = 'the loneliest number' + TWO = 'the number with you' + THREE = 'a triangle' + self.assertEqual(InitNewEnum.THREE.value, 3) + self.assertEqual(InitNewEnum.THREE.description, 'a triangle') + self.assertEqual(InitNewEnum.TWO.proof, 'IN') + # + class InitNewInt(Enum): + _init_ = "value description" + def __new__(cls, value, *args): + member = int.__new__(cls, value) + member._value_ = value + member.proof = 'IN' + return member + # + class InitNewIntEnum(int, InitNewInt): + _order_ = 'ONE TWO THREE' + ONE = 'the loneliest number' + TWO = 'the number with you' + THREE = 'a triangle' + self.assertEqual(InitNewIntEnum.THREE, 3) + self.assertEqual(InitNewIntEnum.THREE.description, 'a triangle') + self.assertEqual(InitNewIntEnum.TWO.proof, 'IN') + # + class InitNewFlagBase(Flag): + _init_ = "value description" + def __new__(cls, value, *args): + member = object.__new__(cls) + member._value_ = value + member.proof = 'IN' + return member + # + class InitNewFlag(InitNewFlagBase): + _order_ = 'ONE TWO THREE' + ONE = 'the loneliest number' + TWO = 'the number with you' + THREE = 'a triangle' + self.assertEqual(InitNewFlag.THREE.value, 4) + self.assertEqual(InitNewFlag.THREE.description, 'a triangle') + self.assertEqual(InitNewFlag.TWO.proof, 'IN') + # + class InitNewIntFlagBase(int, Flag): + _init_ = "value description" + def __new__(cls, value, *args): + member = int.__new__(cls, value) + member._value_ = value + member.proof = 'IN' + return member + # + class InitNewIntFlag(InitNewIntFlagBase): + _order_ = 'ONE TWO THREE' + ONE = 'the loneliest number' + TWO = 'the number with you' + THREE = 'a triangle' + self.assertEqual(InitNewIntFlag.THREE, 4) + self.assertEqual(InitNewIntFlag.THREE.description, 'a triangle') + self.assertEqual(InitNewIntFlag.TWO.proof, 'IN') + + +class TestHelpers(TestCase): + # _is_descriptor, _is_sunder, _is_dunder + + def test_is_descriptor(self): + class foo: + pass + for attr in ('__get__','__set__','__delete__'): + obj = foo() + self.assertFalse(aenum._is_descriptor(obj)) + setattr(obj, attr, 1) + self.assertTrue(aenum._is_descriptor(obj)) + + def test_is_sunder(self): + for s in ('_a_', '_aa_'): + self.assertTrue(aenum._is_sunder(s)) + + for s in ('a', 'a_', '_a', '__a', 'a__', '__a__', '_a__', '__a_', '_', + '__', '___', '____', '_____',): + self.assertFalse(aenum._is_sunder(s)) + + def test_is_dunder(self): + for s in ('__a__', '__aa__'): + self.assertTrue(aenum._is_dunder(s)) + for s in ('a', 'a_', '_a', '__a', 'a__', '_a_', '_a__', '__a_', '_', + '__', '___', '____', '_____',): + self.assertFalse(aenum._is_dunder(s)) + + def test_auto(self): + def tester(first, op, final, second=None): + if second is None: + left = auto() + value = op(left) + left.value = first + self.assertEqual(value.value, final, + "%s %r -> %r != %r" % (op.__name__, first, value, final)) + else: + left = first + right = auto() + value = op(left, right) + right.value = second + self.assertEqual(value.value, final, + "forward: %r %s %r -> %r != %r" % (first, op.__name__, second, value.value, final)) + left = auto() + right = second + value = op(left, right) + left.value = first + self.assertEqual(value.value, final, + "reversed: %r %s %r -> %r != %r" % (second, op.__name__, first, value.value, final)) + for args in ( + (1, _abs_, abs(1)), + (-3, _abs_, abs(-3)), + (1, _add_, 1+2, 2), + (25, _floordiv_, 25 // 5, 5), + (49, _truediv_, 49 / 9, 9), + (6, _mod_, 6 % 9, 9), + (5, _lshift_, 5 << 2, 2), + (5, _rshift_, 5 >> 2, 2), + (3, _mul_, 3 * 6, 6), + (5, _neg_, -5), + (-4, _pos_, +(-4)), + (2, _pow_, 2**5, 5), + (7, _sub_, 7 - 10, 10), + (1, _or_, 1 | 2, 2), + (3, _xor_, 3 ^ 6, 6), + (3, _and_, 3 & 6, 6), + (7, _inv_, ~7), + ('a', _add_, 'a'+'b', 'b'), + ('a', _mul_, 'a' * 3, 3), + ): + tester(*args) + # operator.div is gone in 3 + if PY2: + tester(12, _div_, 12 // 5, 5) + # strings are a pain + left = auto() + right = 'eggs' + value = _mod_(left, right) + left.value = 'I see 17 %s!' + self.assertEqual(value.value, 'I see 17 %s!' % 'eggs') + + def test_constant(self): + errors = [] + def tester(first, op, final, second=None): + if second is None: + primary = constant(first) + secondary = constant(op(primary)) + if secondary.value != final: + errors.append( + "%s %r -> %r != %r" % (op.__name__, first, secondary.value, final), + ) + else: + left = constant(first) + right = second + value = op(left, right) + if value != final: + errors.append( + "forward: %r %s %r -> %r != %r" % (first, op.__name__, second, value, final), + ) + left = first + right = constant(second) + value = op(left, right) + if value != final: + errors.append( + "reversed: %r %s %r -> %r != %r" % (second, op.__name__, first, value, final), + ) + for args in ( + (1, _abs_, abs(1)), + (-3, _abs_, abs(-3)), + (1, _add_, 1+2, 2), + (25, _floordiv_, 25 // 5, 5), + (49, _truediv_, 49 / 9, 9), + (6, _mod_, 6 % 9, 9), + (5, _lshift_, 5 << 2, 2), + (5, _rshift_, 5 >> 2, 2), + (3, _mul_, 3 * 6, 6), + (5, _neg_, -5), + (-4, _pos_, +(-4)), + (2, _pow_, 2**5, 5), + (7, _sub_, 7 - 10, 10), + (1, _or_, 1 | 2, 2), + (3, _xor_, 3 ^ 6, 6), + (3, _and_, 3 & 6, 6), + (7, _inv_, ~7), + ('a', _add_, 'a'+'b', 'b'), + ('a', _mul_, 'a' * 3, 3), + ): + tester(*args) + # operator.div is gone in 3 + if PY2: + tester(12, _div_, 12 // 5, 5) + # strings are a pain + left = constant('I see 17 %s!') + right = 'eggs' + value = _mod_(left, right) + if value != 'I see 17 %s!' % 'eggs': + errors.append("'I see 17 eggs!' != %r" % value) + if errors: + print() + for error in errors: + print(error) + self.assertTrue(False) + + +class TestEnumType(TestCase): + + def test_immutability(self): + class Hah(object): + @classproperty + def all_values(cls): + return [m.value for m in cls] + class Huh(Hah, Enum): + one = 1 + two = 2 + self.assertRaisesRegex(AttributeError, 'cannot rebind property', setattr, Huh, 'value', 'boom') + self.assertRaisesRegex(AttributeError, 'cannot delete property', delattr, Huh, 'value') + self.assertRaisesRegex(AttributeError, 'cannot set attribute', setattr, Huh.one, 'value', 'boom') + self.assertRaisesRegex(AttributeError, 'cannot delete attribute', delattr, Huh.two, 'value') + self.assertEqual(Huh.one.value, 1) + self.assertEqual(Huh.two.value, 2) + self.assertEqual(Huh.all_values, [1, 2]) + setattr(Huh, 'all_values', 99) + self.assertEqual(Huh.all_values, 99) + + def test_enum_shadow_base(self): + class hohum(object): + def cyan(self): + "cyanize a color" + return self.value * 'cyan' + @property + def azure(self): + return 'azure ' + self.name + class Color(hohum, Enum): + red = 1 + green = 2 + blue = 3 + cyan = 4 + azure = 5 + self.assertEqual(len(Color), 5) + self.assertEqual(list(Color), [Color.red, Color.green, Color.blue, Color.cyan, Color.azure]) + self.assertRaisesRegex(AttributeError, 'no attribute .cyan.', lambda: Color.blue.cyan) + self.assertEqual(Color.red.azure, 'azure red') + + +class TestEnum(TestCase): + + def setUp(self): + class Season(Enum): + SPRING = 1 + SUMMER = 2 + AUTUMN = 3 + WINTER = 4 + self.Season = Season + + class Konstants(float, Enum): + E = 2.7182818 + PI = 3.1415926 + TAU = 2 * PI + self.Konstants = Konstants + + class Grades(IntEnum): + A = 5 + B = 4 + C = 3 + D = 2 + F = 0 + self.Grades = Grades + + class Directional(str, Enum): + EAST = 'east' + WEST = 'west' + NORTH = 'north' + SOUTH = 'south' + self.Directional = Directional + + from datetime import date + class Holiday(date, Enum): + NEW_YEAR = 2013, 1, 1 + IDES_OF_MARCH = 2013, 3, 15 + self.Holiday = Holiday + + def test_set_name(self): + class Descriptor(object): + name = None + def __get__(self, instance, owner_class=None): + if instance is None: + return self + else: + return instance.__dict__[self.name] + def __set__(self, instance, value): + instance.__dict__[self.name] = value + def __set_name__(self, owner, name): + self.name = name + # + class AnEnum(Enum): + ONE = 'one' + two = Descriptor() + # + self.assertEqual(list(AnEnum), [AnEnum.ONE]) + self.assertEqual(AnEnum.two.name, 'two') + AnEnum.ONE.two = 'three' + self.assertEqual(AnEnum.ONE.two, 'three') + self.assertEqual(AnEnum.ONE.__dict__['two'], 'three') + + def test_private_names(self): + class Private(Enum): + __corporal = 'Radar' + __major_ = 'Hoolihan' + self.assertEqual(len(Private), 0) + self.assertEqual(Private._Private__corporal, 'Radar') + self.assertFalse(isinstance(Private._Private__corporal, Enum)) + self.assertEqual(Private._Private__major_, 'Hoolihan') + self.assertFalse(isinstance(Private._Private__major_, Enum)) + + def test_new_with_keywords(self): + class Huh(IntEnum): + __order__ = 'PLAIN BOLD_ITALIC HIGHLIGHT' + def __new__(cls, docstring, open=None, close=None): + value = len(cls.__members__) + member = int.__new__(cls, value) + if open and close is None: + close = open + member.open = open + member.close = close + member.__doc__ = docstring + member._value_ = value + return member + PLAIN = 'normal' + BOLD_ITALIC = '***really super important***', '***' + HIGHLIGHT = 'please ==take notice==', '==', '==' + p = Huh.PLAIN + self.assertTrue(type(p) is Huh, type(p)) + self.assertEqual( + (p.value, p.__doc__, p.open, p.close), + (0, 'normal', None, None), + ) + bi = Huh.BOLD_ITALIC + self.assertEqual( + (bi.value, bi.__doc__, bi.open, bi.close), + (1, '***really super important***', '***', '***'), + ) + h = Huh.HIGHLIGHT + self.assertEqual( + (h.value, h.__doc__, h.open, h.close), + (2, 'please ==take notice==', '==', '=='), + ) + + def test_members_is_ordereddict_if_ordered(self): + class Ordered(Enum): + __order__ = 'first second third' + first = 'bippity' + second = 'boppity' + third = 'boo' + self.assertTrue(type(Ordered.__members__) is OrderedDict) + + def test_members_is_ordereddict_if_not_ordered(self): + class Unordered(Enum): + this = 'that' + these = 'those' + self.assertTrue(type(Unordered.__members__) is OrderedDict) + + def test_enum_in_enum_out(self): + Season = self.Season + self.assertTrue(Season(Season.WINTER) is Season.WINTER) + + def test_enum_value(self): + Season = self.Season + self.assertEqual(Season.SPRING.value, 1) + + def test_intenum_value(self): + self.assertEqual(IntStooges.CURLY.value, 2) + + def test_enum(self): + Season = self.Season + lst = list(Season) + self.assertEqual(len(lst), len(Season)) + self.assertEqual(len(Season), 4, Season) + self.assertEqual( + [Season.SPRING, Season.SUMMER, Season.AUTUMN, Season.WINTER], lst) + + for i, season in enumerate('SPRING SUMMER AUTUMN WINTER'.split()): + i += 1 + e = Season(i) + self.assertEqual(e, getattr(Season, season)) + self.assertEqual(e.value, i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, season) + self.assertTrue(e in Season) + self.assertTrue(type(e) is Season) + self.assertTrue(isinstance(e, Season)) + self.assertEqual(str(e), 'Season.' + season) + self.assertEqual( + repr(e), + '' % (season, i), + ) + def test_enum_helper(self): + e1 = enum(1, 2, three=9) + e2 = enum(1, 2, three=9) + e3 = enum(1, 2, 9) + self.assertTrue(e1 is not e2) + self.assertEqual(e1, e2) + self.assertNotEqual(e1, e3) + self.assertNotEqual(e2, e3) + + def test_enum_in_enum(self): + # + class Level(Enum): + _order_ = 'DATA_CHECK DESIGN_CHECK ALERT' + # + def __new__(cls, *args, **kwds): + member = object.__new__(cls) + member._value_ = len(cls) + 1 # members are 1-based + return member + # + def __init__(self, prereq=None, dependent=None): + # create priority level lists + self.lower_priority_levels = list(self.__class__._member_map_.values()) + self.greater_priority_levels = [] + # update previous members' greater priority list + for member in self.lower_priority_levels: + member.greater_priority_levels.append(self) + # and save prereq and dependent + self.prerequisite = prereq and self.__class__[prereq.name] or None + self.dependent = dependent and self.__class__[dependent.name] or None + # + DATA_CHECK = enum() + DESIGN_CHECK = enum(DATA_CHECK) + ALERT = enum(None, DATA_CHECK) + # + self.assertEqual(Level.DATA_CHECK.value, 1) + self.assertEqual(Level.DATA_CHECK.prerequisite, None) + self.assertEqual(Level.DATA_CHECK.dependent, None) + self.assertEqual(Level.DESIGN_CHECK.prerequisite, Level.DATA_CHECK) + self.assertEqual(Level.DESIGN_CHECK.dependent, None) + self.assertEqual(Level.ALERT.prerequisite, None) + self.assertEqual(Level.ALERT.dependent, Level.DATA_CHECK) + + def test_value_name(self): + Season = self.Season + self.assertEqual(Season.SPRING.name, 'SPRING') + self.assertEqual(Season.SPRING.value, 1) + def set_name(obj, new_value): + obj.name = new_value + def set_value(obj, new_value): + obj.value = new_value + self.assertRaises(AttributeError, set_name, Season.SPRING, 'invierno', ) + self.assertRaises(AttributeError, set_value, Season.SPRING, 2) + + def test_attribute_deletion(self): + class Season(Enum): + SPRING = 1 + SUMMER = 2 + AUTUMN = 3 + WINTER = 4 + + def spam(cls): + pass + + self.assertTrue(hasattr(Season, 'spam')) + del Season.spam + self.assertFalse(hasattr(Season, 'spam')) + + self.assertRaises(AttributeError, delattr, Season, 'SPRING') + self.assertRaises(AttributeError, delattr, Season, 'DRY') + self.assertRaises(AttributeError, delattr, Season.SPRING, 'name') + + def test_bool_of_class(self): + class Empty(Enum): + pass + self.assertTrue(bool(Empty)) + + def test_bool_of_member(self): + class Count(Enum): + zero = 0 + one = 1 + two = 2 + for member in Count: + self.assertTrue(bool(member)) + + def test_invalid_names(self): + def create_bad_class_1(): + class Wrong(Enum): + mro = 9 + def create_bad_class_2(): + class Wrong(Enum): + _reserved_ = 3 + self.assertRaises(ValueError, create_bad_class_1) + self.assertRaises(ValueError, create_bad_class_2) + + def test_bool(self): + class Logic(Enum): + true = True + false = False + def __bool__(self): + return bool(self.value) + __nonzero__ = __bool__ + self.assertTrue(Logic.true) + self.assertFalse(Logic.false) + + def test_contains(self): + Season = self.Season + self.assertRaises(TypeError, lambda: 'AUTUMN' in Season) + self.assertTrue(Season.AUTUMN in Season) + self.assertRaises(TypeError, lambda: 3 not in Season) + val = Season(3) + self.assertTrue(val in Season) + # + class OtherEnum(Enum): + one = 1; two = 2 + self.assertTrue(OtherEnum.two not in Season) + # + class Wierd(Enum): + this = [1, 2, 3] + that = (1, 2, 3) + those = {1: 1, 2: 2, 3: 3} + self.assertTrue(Wierd.this in Wierd) + self.assertRaises(TypeError, lambda: [1, 2, 3] in Wierd) + self.assertRaises(TypeError, lambda: {1: 1, 2: 2, 3: 3} in Wierd) + + def test_member_contains(self): + self.assertRaises(TypeError, lambda: 'test' in self.Season.AUTUMN) + + if pyver >= PY2_6: # when `format` came into being + + def test_format_enum(self): + Season = self.Season + self.assertEqual('{0}'.format(Season.SPRING), + '{0}'.format(str(Season.SPRING))) + self.assertEqual( '{0:}'.format(Season.SPRING), + '{0:}'.format(str(Season.SPRING))) + self.assertEqual('{0:20}'.format(Season.SPRING), + '{0:20}'.format(str(Season.SPRING))) + self.assertEqual('{0:^20}'.format(Season.SPRING), + '{0:^20}'.format(str(Season.SPRING))) + self.assertEqual('{0:>20}'.format(Season.SPRING), + '{0:>20}'.format(str(Season.SPRING))) + self.assertEqual('{0:<20}'.format(Season.SPRING), + '{0:<20}'.format(str(Season.SPRING))) + + def test_custom_format(self): + class TestFloat(float, Enum): + one = 1.0 + two = 2.0 + def __format__(self, spec): + return 'TestFloat success!' + self.assertEqual(str(TestFloat.one), 'TestFloat.one') + self.assertEqual('{0}'.format(TestFloat.one), 'TestFloat success!') + + def test_format_with_custom_str(self): + class TestInt(int, Enum): + one = 1 + two = 2 + def __str__(self): + return self.name * 3 + self.assertEqual(str(TestInt.two), 'twotwotwo') + self.assertEqual('{0}'.format(TestInt.two), 'twotwotwo') + + def assertFormatIsValue(self, spec, member): + self.assertEqual(spec.format(member), spec.format(member.value)) + + def test_format_enum_date(self): + Holiday = self.Holiday + self.assertFormatIsValue('{0}', Holiday.IDES_OF_MARCH) + self.assertFormatIsValue('{0:}', Holiday.IDES_OF_MARCH) + self.assertFormatIsValue('{0:20}', Holiday.IDES_OF_MARCH) + self.assertFormatIsValue('{0:^20}', Holiday.IDES_OF_MARCH) + self.assertFormatIsValue('{0:>20}', Holiday.IDES_OF_MARCH) + self.assertFormatIsValue('{0:<20}', Holiday.IDES_OF_MARCH) + self.assertFormatIsValue('{0:%Y %m}', Holiday.IDES_OF_MARCH) + self.assertFormatIsValue('{0:%Y %m %M:00}', Holiday.IDES_OF_MARCH) + + def test_format_enum_float(self): + Konstants = self.Konstants + self.assertFormatIsValue('{0}', Konstants.TAU) + self.assertFormatIsValue('{0:}', Konstants.TAU) + self.assertFormatIsValue('{0:20}', Konstants.TAU) + self.assertFormatIsValue('{0:^20}', Konstants.TAU) + self.assertFormatIsValue('{0:>20}', Konstants.TAU) + self.assertFormatIsValue('{0:<20}', Konstants.TAU) + self.assertFormatIsValue('{0:n}', Konstants.TAU) + self.assertFormatIsValue('{0:5.2}', Konstants.TAU) + self.assertFormatIsValue('{0:f}', Konstants.TAU) + + def test_format_enum_int(self): + Grades = self.Grades + self.assertFormatIsValue('{0}', Grades.C) + self.assertFormatIsValue('{0:}', Grades.C) + self.assertFormatIsValue('{0:20}', Grades.C) + self.assertFormatIsValue('{0:^20}', Grades.C) + self.assertFormatIsValue('{0:>20}', Grades.C) + self.assertFormatIsValue('{0:<20}', Grades.C) + self.assertFormatIsValue('{0:+}', Grades.C) + self.assertFormatIsValue('{0:08X}', Grades.C) + self.assertFormatIsValue('{0:b}', Grades.C) + + def test_format_enum_str(self): + Directional = self.Directional + self.assertFormatIsValue('{0}', Directional.WEST) + self.assertFormatIsValue('{0:}', Directional.WEST) + self.assertFormatIsValue('{0:20}', Directional.WEST) + self.assertFormatIsValue('{0:^20}', Directional.WEST) + self.assertFormatIsValue('{0:>20}', Directional.WEST) + self.assertFormatIsValue('{0:<20}', Directional.WEST) + + def test_hash(self): + Season = self.Season + dates = {} + dates[Season.WINTER] = '1225' + dates[Season.SPRING] = '0315' + dates[Season.SUMMER] = '0704' + dates[Season.AUTUMN] = '1031' + self.assertEqual(dates[Season.AUTUMN], '1031') + + def test_enum_duplicates(self): + class Season(Enum): + __order__ = "SPRING SUMMER AUTUMN WINTER" + SPRING = 1 + SUMMER = 2 + AUTUMN = FALL = 3 + WINTER = 4 + ANOTHER_SPRING = 1 + lst = list(Season) + self.assertEqual( + lst, + [Season.SPRING, Season.SUMMER, + Season.AUTUMN, Season.WINTER, + ]) + self.assertTrue(Season.FALL is Season.AUTUMN) + self.assertEqual(Season.FALL.value, 3) + self.assertEqual(Season.AUTUMN.value, 3) + self.assertTrue(Season(3) is Season.AUTUMN) + self.assertTrue(Season(1) is Season.SPRING) + self.assertEqual(Season.FALL.name, 'AUTUMN') + self.assertEqual( + set([k for k,v in Season.__members__.items() if v.name != k]), + set(['FALL', 'ANOTHER_SPRING']), + ) + + def test_enum_with_value_name(self): + class Huh(Enum): + _order_ = 'name value' + name = 1 + value = 2 + self.assertEqual( + list(Huh), + [Huh.name, Huh.value], + ) + self.assertTrue(type(Huh.name) is Huh) + self.assertEqual(Huh.name.name, 'name') + self.assertEqual(Huh.name.value, 1) + + def test_intenum_from_scratch(self): + class phy(int, Enum): + pi = 3 + tau = 2 * pi + self.assertTrue(phy.pi < phy.tau) + + def test_intenum_inherited(self): + class IntEnum(int, Enum): + pass + class phy(IntEnum): + pi = 3 + tau = 2 * pi + self.assertTrue(phy.pi < phy.tau) + + def test_floatenum_from_scratch(self): + class phy(float, Enum): + pi = 3.1415926 + tau = 2 * pi + self.assertTrue(phy.pi < phy.tau) + + def test_floatenum_inherited(self): + class FloatEnum(float, Enum): + pass + class phy(FloatEnum): + pi = 3.1415926 + tau = 2 * pi + self.assertTrue(phy.pi < phy.tau) + + def test_strenum_from_scratch(self): + class phy(str, Enum): + pi = 'Pi' + tau = 'Tau' + self.assertTrue(phy.pi < phy.tau) + + def test_intenum(self): + class WeekDay(IntEnum): + SUNDAY = 1 + MONDAY = 2 + TUESDAY = 3 + WEDNESDAY = 4 + THURSDAY = 5 + FRIDAY = 6 + SATURDAY = 7 + + self.assertEqual(['a', 'b', 'c'][WeekDay.MONDAY], 'c') + self.assertEqual([i for i in range(WeekDay.TUESDAY)], [0, 1, 2]) + + lst = list(WeekDay) + self.assertEqual(len(lst), len(WeekDay)) + self.assertEqual(len(WeekDay), 7) + target = 'SUNDAY MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY' + target = target.split() + for i, weekday in enumerate(target): + i += 1 + e = WeekDay(i) + self.assertEqual(e, i) + self.assertEqual(int(e), i) + self.assertEqual(e.name, weekday) + self.assertTrue(e in WeekDay) + self.assertEqual(lst.index(e)+1, i) + self.assertTrue(0 < e < 8) + self.assertTrue(type(e) is WeekDay) + self.assertTrue(isinstance(e, int)) + self.assertTrue(isinstance(e, Enum)) + + def test_intenum_duplicates(self): + class WeekDay(IntEnum): + __order__ = 'SUNDAY MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY' + SUNDAY = 1 + MONDAY = 2 + TUESDAY = TEUSDAY = 3 + WEDNESDAY = 4 + THURSDAY = 5 + FRIDAY = 6 + SATURDAY = 7 + self.assertTrue(WeekDay.TEUSDAY is WeekDay.TUESDAY) + self.assertEqual(WeekDay(3).name, 'TUESDAY') + self.assertEqual([k for k,v in WeekDay.__members__.items() + if v.name != k], ['TEUSDAY', ]) + + def test_floatenum_fromhex(self): + h = float.hex(FloatStooges.MOE.value) + self.assertIs(FloatStooges.fromhex(h), FloatStooges.MOE) + h = float.hex(FloatStooges.MOE.value + 0.01) + with self.assertRaises(ValueError): + FloatStooges.fromhex(h) + + def test_pickle_enum(self): + if isinstance(Stooges, Exception): + raise Stooges + test_pickle_dump_load(self.assertTrue, Stooges.CURLY) + test_pickle_dump_load(self.assertTrue, Stooges) + + def test_pickle_int(self): + if isinstance(IntStooges, Exception): + raise IntStooges + test_pickle_dump_load(self.assertTrue, IntStooges.CURLY) + test_pickle_dump_load(self.assertTrue, IntStooges) + + def test_pickle_float(self): + if isinstance(FloatStooges, Exception): + raise FloatStooges + test_pickle_dump_load(self.assertTrue, FloatStooges.CURLY) + test_pickle_dump_load(self.assertTrue, FloatStooges) + + def test_pickle_enum_function(self): + if isinstance(Answer, Exception): + raise Answer + test_pickle_dump_load(self.assertTrue, Answer.him) + test_pickle_dump_load(self.assertTrue, Answer) + + def test_pickle_enum_function_with_module(self): + if isinstance(Question, Exception): + raise Question + test_pickle_dump_load(self.assertTrue, Question.who) + test_pickle_dump_load(self.assertTrue, Question) + + def test_pickle_by_name(self): + class ReplaceGlobalInt(IntEnum): + ONE = 1 + TWO = 2 + ReplaceGlobalInt.__reduce_ex__ = _reduce_ex_by_name + for proto in range(HIGHEST_PROTOCOL): + self.assertEqual(ReplaceGlobalInt.TWO.__reduce_ex__(proto), 'TWO') + + def test_exploding_pickle(self): + BadPickle = Enum('BadPickle', 'dill sweet bread-n-butter') + aenum._make_class_unpicklable(BadPickle) + globals()['BadPickle'] = BadPickle + test_pickle_exception(self.assertRaises, TypeError, BadPickle.dill) + test_pickle_exception(self.assertRaises, PicklingError, BadPickle) + + def test_string_enum(self): + class SkillLevel(str, Enum): + master = 'what is the sound of one hand clapping?' + journeyman = 'why did the chicken cross the road?' + apprentice = 'knock, knock!' + self.assertEqual(SkillLevel.apprentice, 'knock, knock!') + + def test_getattr_getitem(self): + class Period(Enum): + morning = 1 + noon = 2 + evening = 3 + night = 4 + self.assertTrue(Period(2) is Period.noon) + self.assertTrue(getattr(Period, 'night') is Period.night) + self.assertTrue(Period['morning'] is Period.morning) + + def test_getattr_dunder(self): + Season = self.Season + self.assertTrue(getattr(Season, '__hash__')) + + def test_iteration_order(self): + class Season(Enum): + __order__ = 'SUMMER WINTER AUTUMN SPRING' + SUMMER = 2 + WINTER = 4 + AUTUMN = 3 + SPRING = 1 + self.assertEqual( + list(Season), + [Season.SUMMER, Season.WINTER, Season.AUTUMN, Season.SPRING], + ) + + def test_iteration_order_reversed(self): + self.assertEqual( + list(reversed(self.Season)), + [self.Season.WINTER, self.Season.AUTUMN, self.Season.SUMMER, + self.Season.SPRING] + ) + + def test_iteration_order_with_unorderable_values(self): + class Complex(Enum): + a = complex(7, 9) + b = complex(3.14, 2) + c = complex(1, -1) + d = complex(-77, 32) + self.assertEqual( + list(Complex), + [Complex.a, Complex.b, Complex.c, Complex.d], + ) + + def test_programatic_function_string(self): + SummerMonth = Enum('SummerMonth', 'june july august') + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split()): + i += 1 + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertTrue(e in SummerMonth) + self.assertTrue(type(e) is SummerMonth) + + def test_programatic_function_string_with_start(self): + SummerMonth = Enum('SummerMonth', 'june july august', start=10) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 10): + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertTrue(e in SummerMonth) + self.assertTrue(type(e) is SummerMonth) + + def test_programatic_function_string_list(self): + SummerMonth = Enum('SummerMonth', ['june', 'july', 'august']) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split()): + i += 1 + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertTrue(e in SummerMonth) + self.assertTrue(type(e) is SummerMonth) + + def test_programatic_function_string_list_with_start(self): + SummerMonth = Enum('SummerMonth', ['june', 'july', 'august'], start=20) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 20): + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertTrue(e in SummerMonth) + self.assertTrue(type(e) is SummerMonth) + + def test_programatic_function_iterable(self): + SummerMonth = Enum( + 'SummerMonth', + (('june', 1), ('july', 2), ('august', 3)) + ) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split()): + i += 1 + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertTrue(e in SummerMonth) + self.assertTrue(type(e) is SummerMonth) + + def test_programatic_function_from_dict(self): + SummerMonth = Enum( + 'SummerMonth', + dict((('june', 1), ('july', 2), ('august', 3))) + ) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + if PY2: + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split()): + i += 1 + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertTrue(e in SummerMonth) + self.assertTrue(type(e) is SummerMonth) + + def test_programatic_function_type(self): + SummerMonth = Enum('SummerMonth', 'june july august', type=int) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split()): + i += 1 + e = SummerMonth(i) + self.assertEqual(e, i) + self.assertEqual(e.name, month) + self.assertTrue(e in SummerMonth) + self.assertTrue(type(e) is SummerMonth) + + def test_programatic_function_type_with_start(self): + SummerMonth = Enum('SummerMonth', 'june july august', type=int, start=30) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 30): + e = SummerMonth(i) + self.assertEqual(e, i) + self.assertEqual(e.name, month) + self.assertTrue(e in SummerMonth) + self.assertTrue(type(e) is SummerMonth) + + def test_programatic_function_type_from_subclass(self): + SummerMonth = IntEnum('SummerMonth', 'june july august') + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split()): + i += 1 + e = SummerMonth(i) + self.assertEqual(e, i) + self.assertEqual(e.name, month) + self.assertTrue(e in SummerMonth) + self.assertTrue(type(e) is SummerMonth) + + def test_programatic_function_type_from_subclass_with_start(self): + SummerMonth = IntEnum('SummerMonth', 'june july august', start=40) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate('june july august'.split(), 40): + e = SummerMonth(i) + self.assertEqual(e, i) + self.assertEqual(e.name, month) + self.assertTrue(e in SummerMonth) + self.assertTrue(type(e) is SummerMonth) + + def test_programatic_function_unicode(self): + SummerMonth = Enum('SummerMonth', unicode('june july august')) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate(unicode('june july august').split()): + i += 1 + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertTrue(e in SummerMonth) + self.assertTrue(type(e) is SummerMonth) + + def test_programatic_function_unicode_list(self): + SummerMonth = Enum('SummerMonth', [unicode('june'), unicode('july'), unicode('august')]) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate(unicode('june july august').split()): + i += 1 + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertTrue(e in SummerMonth) + self.assertTrue(type(e) is SummerMonth) + + def test_programatic_function_unicode_iterable(self): + SummerMonth = Enum( + 'SummerMonth', + ((unicode('june'), 1), (unicode('july'), 2), (unicode('august'), 3)) + ) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate(unicode('june july august').split()): + i += 1 + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertTrue(e in SummerMonth) + self.assertTrue(type(e) is SummerMonth) + + def test_programatic_function_from_unicode_dict(self): + SummerMonth = Enum( + 'SummerMonth', + dict(((unicode('june'), 1), (unicode('july'), 2), (unicode('august'), 3))) + ) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + if PY2: + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate(unicode('june july august').split()): + i += 1 + e = SummerMonth(i) + self.assertEqual(int(e.value), i) + self.assertNotEqual(e, i) + self.assertEqual(e.name, month) + self.assertTrue(e in SummerMonth) + self.assertTrue(type(e) is SummerMonth) + + def test_programatic_function_unicode_type(self): + SummerMonth = Enum('SummerMonth', unicode('june july august'), type=int) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate(unicode('june july august').split()): + i += 1 + e = SummerMonth(i) + self.assertEqual(e, i) + self.assertEqual(e.name, month) + self.assertTrue(e in SummerMonth) + self.assertTrue(type(e) is SummerMonth) + + def test_programatic_function_unicode_type_from_subclass(self): + SummerMonth = IntEnum('SummerMonth', unicode('june july august')) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate(unicode('june july august').split()): + i += 1 + e = SummerMonth(i) + self.assertEqual(e, i) + self.assertEqual(e.name, month) + self.assertTrue(e in SummerMonth) + self.assertTrue(type(e) is SummerMonth) + + def test_programmatic_function_unicode_class(self): + if PY2: + class_names = unicode('SummerMonth'), 'S\xfcmm\xe9rM\xf6nth'.decode('latin1') + else: + class_names = 'SummerMonth', 'S\xfcmm\xe9rM\xf6nth' + for i, class_name in enumerate(class_names): + if PY2 and i == 1: + self.assertRaises(TypeError, Enum, class_name, unicode('june july august')) + else: + SummerMonth = Enum(class_name, unicode('june july august')) + lst = list(SummerMonth) + self.assertEqual(len(lst), len(SummerMonth)) + self.assertEqual(len(SummerMonth), 3, SummerMonth) + self.assertEqual( + [SummerMonth.june, SummerMonth.july, SummerMonth.august], + lst, + ) + for i, month in enumerate(unicode('june july august').split()): + i += 1 + e = SummerMonth(i) + self.assertEqual(e.value, i) + self.assertEqual(e.name, month) + self.assertTrue(e in SummerMonth) + self.assertTrue(type(e) is SummerMonth) + + def test_subclassing(self): + if isinstance(Name, Exception): + raise Name + self.assertEqual(Name.BDFL, 'Guido van Rossum') + self.assertTrue(Name.BDFL, Name('Guido van Rossum')) + self.assertTrue(Name.BDFL is getattr(Name, 'BDFL')) + test_pickle_dump_load(self.assertTrue, Name.BDFL) + + def test_extending(self): + def bad_extension(): + class Color(Enum): + red = 1 + green = 2 + blue = 3 + class MoreColor(Color): + cyan = 4 + magenta = 5 + yellow = 6 + self.assertRaises(TypeError, bad_extension) + + def test_exclude_methods(self): + class whatever(Enum): + this = 'that' + these = 'those' + def really(self): + return 'no, not %s' % self.value + self.assertFalse(type(whatever.really) is whatever) + self.assertEqual(whatever.this.really(), 'no, not that') + + def test_wrong_inheritance_order(self): + def wrong_inherit(): + class Wrong(Enum, str): + NotHere = 'error before this point' + self.assertRaises(TypeError, wrong_inherit) + + def test_intenum_transitivity(self): + class number(IntEnum): + one = 1 + two = 2 + three = 3 + class numero(IntEnum): + uno = 1 + dos = 2 + tres = 3 + self.assertEqual(number.one, numero.uno) + self.assertEqual(number.two, numero.dos) + self.assertEqual(number.three, numero.tres) + + def test_introspection(self): + class Number(IntEnum): + one = 100 + two = 200 + self.assertTrue(Number.one._member_type_ is int) + self.assertTrue(Number._member_type_ is int) + class String(str, Enum): + yarn = 'soft' + rope = 'rough' + wire = 'hard' + self.assertTrue(String.yarn._member_type_ is str) + self.assertTrue(String._member_type_ is str) + class Plain(Enum): + vanilla = 'white' + one = 1 + self.assertTrue(Plain.vanilla._member_type_ is object) + self.assertTrue(Plain._member_type_ is object) + + def test_wrong_enum_in_call(self): + class Monochrome(Enum): + black = 0 + white = 1 + class Gender(Enum): + male = 0 + female = 1 + self.assertRaises(ValueError, Monochrome, Gender.male) + + def test_wrong_enum_in_mixed_call(self): + class Monochrome(IntEnum): + black = 0 + white = 1 + class Gender(Enum): + male = 0 + female = 1 + self.assertRaises(ValueError, Monochrome, Gender.male) + + def test_mixed_enum_in_call_1(self): + class Monochrome(IntEnum): + black = 0 + white = 1 + class Gender(IntEnum): + male = 0 + female = 1 + self.assertTrue(Monochrome(Gender.female) is Monochrome.white) + + def test_mixed_enum_in_call_2(self): + class Monochrome(Enum): + black = 0 + white = 1 + class Gender(IntEnum): + male = 0 + female = 1 + self.assertTrue(Monochrome(Gender.male) is Monochrome.black) + + def test_flufl_enum(self): + class Fluflnum(Enum): + def __int__(self): + return int(self.value) + class MailManOptions(Fluflnum): + option1 = 1 + option2 = 2 + option3 = 3 + self.assertEqual(int(MailManOptions.option1), 1) + + def test_no_such_enum_member(self): + class Color(Enum): + red = 1 + green = 2 + blue = 3 + self.assertRaises(ValueError, Color, 4) + self.assertRaises(KeyError, Color.__getitem__, 'chartreuse') + + def test_new_repr(self): + class Color(Enum): + red = 1 + green = 2 + blue = 3 + def __repr__(self): + return "don't you just love shades of %s?" % self.name + self.assertEqual( + repr(Color.blue), + "don't you just love shades of blue?", + ) + + def test_inherited_repr(self): + class MyEnum(Enum): + def __repr__(self): + return "My name is %s." % self.name + class MyIntEnum(int, MyEnum): + this = 1 + that = 2 + theother = 3 + self.assertEqual(repr(MyIntEnum.that), "My name is that.") + + def test_multiple_mixin_mro(self): + class auto_enum(EnumMeta): + def __new__(metacls, cls, bases, classdict): + original_dict = classdict + temp_dict = metacls.__prepare__(cls, bases, {}) + if hasattr(original_dict, '_member_names'): + for k in original_dict._member_names: + temp_dict[k] = original_dict[k] + sunders = [k for k in original_dict.keys() if aenum._is_sunder(k)] + else: + sunders = [] + for k, v in original_dict.items(): + if aenum._is_sunder(k): + sunders.append(k) + temp_dict[k] = v + classdict = metacls.__prepare__(cls, bases, {}) + i = 0 + for k in sunders: + classdict[k] = original_dict[k] + for k in temp_dict._member_names: + v = original_dict[k] + if v == (): + v = i + else: + i = v + i += 1 + classdict[k] = v + for k, v in original_dict.items(): + if k not in temp_dict._member_names and k not in sunders: + classdict[k] = v + return super(auto_enum, metacls).__new__( + metacls, cls, bases, classdict) + + AutoNumberedEnum = auto_enum('AutoNumberedEnum', (Enum,), {}) + + AutoIntEnum = auto_enum('AutoIntEnum', (IntEnum,), {}) + + # class TestAutoNumber(AutoNumberedEnum): + # a = () + # b = 3 + # c = () + # self.assertEqual(TestAutoNumber.b.value, 3) + # + # if pyver >= 3.0: + # self.assertEqual( + # [TestAutoNumber.a.value, TestAutoNumber.b.value, TestAutoNumber.c.value], + # [0, 3, 4], + # ) + # + # class TestAutoInt(AutoIntEnum): + # a = () + # b = 3 + # c = () + # self.assertEqual(TestAutoInt.b, 3) + # + # if pyver >= 3.0: + # self.assertEqual( + # [TestAutoInt.a.value, TestAutoInt.b.value, TestAutoInt.c.value], + # [0, 3, 4], + # ) + + def test_meta_reconfigure(self): + + def identity(*args): + if len(args) == 1: + return args[0] + return args + + JSONEnum = None + + class JSONEnumMeta(EnumMeta): + + @classmethod + def __prepare__(metacls, cls, bases, init=None, start=None, settings=()): + return {} + + def __init__(cls, *args , **kwds): + super(JSONEnumMeta, cls).__init__(*args) + + def __new__(metacls, cls, bases, clsdict, init=None, start=None, settings=()): + import json + members = [] + if JSONEnum is not None: + if '_file' not in clsdict: + raise TypeError('_file is required') + if '_name' not in clsdict: + raise TypeError('_name is required') + if '_value' not in clsdict: + raise TypeError('_value is required') + name_spec = clsdict.pop('_name') + if not isinstance(name_spec, (tuple, list)): + name_spec = (name_spec, ) + value_spec = clsdict.pop('_value') + file = clsdict.pop('_file') + with open(file) as f: + json_data = json.load(f) + for data in json_data: + values = [] + name = data[name_spec[0]] + for piece in name_spec[1:]: + name = name[piece] + for order, (value_path, func) in sorted(value_spec.items()): + if not isinstance(value_path, (list, tuple)): + value_path = (value_path, ) + value = data[value_path[0]] + for piece in value_path[1:]: + value = value[piece] + if func is not None: + value = func(value) + values.append(value) + values = tuple(values) + members.append( + (name, identity(*values)) + ) + # get the real EnumDict + enum_dict = super(JSONEnumMeta, metacls).__prepare__(cls, bases, init, start, settings) + # transfer the original dict content, _items first + items = list(clsdict.items()) + items.sort(key=lambda p: (0 if p[0][0] == '_' else 1, p)) + for name, value in items: + enum_dict[name] = value + # add the members + for name, value in members: + enum_dict[name] = value + return super(JSONEnumMeta, metacls).__new__(metacls, cls, bases, enum_dict, init, start, settings) + + # for use with both Python 2/3 + JSONEnum = JSONEnumMeta('JsonEnum', (Enum, ), {}) + + test_file = os.path.join(tempdir, 'test_json.json') + with open(test_file, 'w') as f: + f.write( + '[{"name":"Afghanistan","alpha-2":"AF","country-code":"004","notes":{"description":"pretty"}},' + '{"name":"Åland Islands","alpha-2":"AX","country-code":"248","notes":{"description":"serene"}},' + '{"name":"Albania","alpha-2":"AL","country-code":"008","notes":{"description":"exciting"}},' + '{"name":"Algeria","alpha-2":"DZ","country-code":"012","notes":{"description":"scarce"}}]') + + class Country(JSONEnum): + _init_ = 'abbr code country_name description' + _file = test_file + _name = 'alpha-2' + _value = { + 1: ('alpha-2', None), + 2: ('country-code', lambda c: int(c)), + 3: ('name', None), + 4: (('notes','description'), lambda s: s.title()), + } + + self.assertEqual([Country.AF, Country.AX, Country.AL, Country.DZ], list(Country)) + self.assertEqual(Country.AF.abbr, 'AF') + self.assertEqual(Country.AX.code, 248) + self.assertEqual(Country.AL.country_name, 'Albania') + self.assertEqual(Country.DZ.description, 'Scarce') + + + def test_subclasses_with_getnewargs(self): + class NamedInt(int): + __qualname__ = 'NamedInt' # needed for pickle protocol 4 + def __new__(cls, *args): + _args = args + if len(args) < 1: + raise TypeError("name and value must be specified") + name, args = args[0], args[1:] + self = int.__new__(cls, *args) + self._intname = name + self._args = _args + return self + def __getnewargs__(self): + return self._args + @property + def __name__(self): + return self._intname + def __repr__(self): + # repr() is updated to include the name and type info + return "%s(%r, %s)" % (type(self).__name__, + self.__name__, + int.__repr__(self)) + def __str__(self): + # str() is unchanged, even if it relies on the repr() fallback + base = int + base_str = base.__str__ + if base_str.__objclass__ is object: + return base.__repr__(self) + return base_str(self) + # for simplicity, we only define one operator that + # propagates expressions + def __add__(self, other): + temp = int(self) + int( other) + if isinstance(self, NamedInt) and isinstance(other, NamedInt): + return NamedInt( + '(%s + %s)' % (self.__name__, other.__name__), + temp ) + else: + return temp + + class NEI(NamedInt, Enum): + __qualname__ = 'NEI' # needed for pickle protocol 4 + x = ('the-x', 1) + y = ('the-y', 2) + + self.assertTrue(NEI.__new__ is Enum.__new__) + self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") + globals()['NamedInt'] = NamedInt + globals()['NEI'] = NEI + NI5 = NamedInt('test', 5) + self.assertEqual(NI5, 5) + test_pickle_dump_load(self.assertTrue, NI5, 5) + self.assertEqual(NEI.y.value, 2) + test_pickle_dump_load(self.assertTrue, NEI.y) + + def test_subclasses_with_reduce(self): + class NamedInt(int): + __qualname__ = 'NamedInt' # needed for pickle protocol 4 + def __new__(cls, *args): + _args = args + if len(args) < 1: + raise TypeError("name and value must be specified") + name, args = args[0], args[1:] + self = int.__new__(cls, *args) + self._intname = name + self._args = _args + return self + def __reduce__(self): + return self.__class__, self._args + @property + def __name__(self): + return self._intname + def __repr__(self): + # repr() is updated to include the name and type info + return "%s(%r, %s)" % (type(self).__name__, + self.__name__, + int.__repr__(self)) + def __str__(self): + # str() is unchanged, even if it relies on the repr() fallback + base = int + base_str = base.__str__ + if base_str.__objclass__ is object: + return base.__repr__(self) + return base_str(self) + # for simplicity, we only define one operator that + # propagates expressions + def __add__(self, other): + temp = int(self) + int( other) + if isinstance(self, NamedInt) and isinstance(other, NamedInt): + return NamedInt( + '(%s + %s)' % (self.__name__, other.__name__), + temp ) + else: + return temp + + class NEI(NamedInt, Enum): + __qualname__ = 'NEI' # needed for pickle protocol 4 + x = ('the-x', 1) + y = ('the-y', 2) + + + self.assertTrue(NEI.__new__ is Enum.__new__) + self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") + globals()['NamedInt'] = NamedInt + globals()['NEI'] = NEI + NI5 = NamedInt('test', 5) + self.assertEqual(NI5, 5) + test_pickle_dump_load(self.assertEqual, NI5, 5) + self.assertEqual(NEI.y.value, 2) + test_pickle_dump_load(self.assertTrue, NEI.y) + + def test_subclasses_with_reduce_ex(self): + class NamedInt(int): + __qualname__ = 'NamedInt' # needed for pickle protocol 4 + def __new__(cls, *args): + _args = args + if len(args) < 1: + raise TypeError("name and value must be specified") + name, args = args[0], args[1:] + self = int.__new__(cls, *args) + self._intname = name + self._args = _args + return self + def __reduce_ex__(self, proto): + return self.__class__, self._args + @property + def __name__(self): + return self._intname + def __repr__(self): + # repr() is updated to include the name and type info + return "%s(%r, %s)" % (type(self).__name__, + self.__name__, + int.__repr__(self)) + def __str__(self): + # str() is unchanged, even if it relies on the repr() fallback + base = int + base_str = base.__str__ + if base_str.__objclass__ is object: + return base.__repr__(self) + return base_str(self) + # for simplicity, we only define one operator that + # propagates expressions + def __add__(self, other): + temp = int(self) + int( other) + if isinstance(self, NamedInt) and isinstance(other, NamedInt): + return NamedInt( + '(%s + %s)' % (self.__name__, other.__name__), + temp ) + else: + return temp + + class NEI(NamedInt, Enum): + __qualname__ = 'NEI' # needed for pickle protocol 4 + x = ('the-x', 1) + y = ('the-y', 2) + + + self.assertTrue(NEI.__new__ is Enum.__new__) + self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") + globals()['NamedInt'] = NamedInt + globals()['NEI'] = NEI + NI5 = NamedInt('test', 5) + self.assertEqual(NI5, 5) + test_pickle_dump_load(self.assertEqual, NI5, 5) + self.assertEqual(NEI.y.value, 2) + test_pickle_dump_load(self.assertTrue, NEI.y) + + def test_subclasses_without_direct_pickle_support(self): + class NamedInt(int): + __qualname__ = 'NamedInt' + def __new__(cls, *args): + _args = args + name, args = args[0], args[1:] + if len(args) == 0: + raise TypeError("name and value must be specified") + self = int.__new__(cls, *args) + self._intname = name + self._args = _args + return self + @property + def __name__(self): + return self._intname + def __repr__(self): + # repr() is updated to include the name and type info + return "%s(%r, %s)" % (type(self).__name__, + self.__name__, + int.__repr__(self)) + def __str__(self): + # str() is unchanged, even if it relies on the repr() fallback + base = int + base_str = base.__str__ + if base_str.__objclass__ is object: + return base.__repr__(self) + return base_str(self) + # for simplicity, we only define one operator that + # propagates expressions + def __add__(self, other): + temp = int(self) + int( other) + if isinstance(self, NamedInt) and isinstance(other, NamedInt): + return NamedInt( + '(%s + %s)' % (self.__name__, other.__name__), + temp ) + else: + return temp + + class NEI(NamedInt, Enum): + __qualname__ = 'NEI' + x = ('the-x', 1) + y = ('the-y', 2) + + self.assertTrue(NEI.__new__ is Enum.__new__) + self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") + globals()['NamedInt'] = NamedInt + globals()['NEI'] = NEI + NI5 = NamedInt('test', 5) + self.assertEqual(NI5, 5) + self.assertEqual(NEI.y.value, 2) + test_pickle_exception(self.assertRaises, TypeError, NEI.x) + test_pickle_exception(self.assertRaises, PicklingError, NEI) + + def test_subclasses_without_direct_pickle_support_using_name(self): + class NamedInt(int): + __qualname__ = 'NamedInt' + def __new__(cls, *args): + _args = args + name, args = args[0], args[1:] + if len(args) == 0: + raise TypeError("name and value must be specified") + self = int.__new__(cls, *args) + self._intname = name + self._args = _args + return self + @property + def __name__(self): + return self._intname + def __repr__(self): + # repr() is updated to include the name and type info + return "%s(%r, %s)" % (type(self).__name__, + self.__name__, + int.__repr__(self)) + def __str__(self): + # str() is unchanged, even if it relies on the repr() fallback + base = int + base_str = base.__str__ + if base_str.__objclass__ is object: + return base.__repr__(self) + return base_str(self) + # for simplicity, we only define one operator that + # propagates expressions + def __add__(self, other): + temp = int(self) + int( other) + if isinstance(self, NamedInt) and isinstance(other, NamedInt): + return NamedInt( + '(%s + %s)' % (self.__name__, other.__name__), + temp ) + else: + return temp + + class NEI(NamedInt, Enum): + __qualname__ = 'NEI' + x = ('the-x', 1) + y = ('the-y', 2) + def __reduce_ex__(self, proto): + return getattr, (self.__class__, self._name_) + + self.assertTrue(NEI.__new__ is Enum.__new__) + self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") + globals()['NamedInt'] = NamedInt + globals()['NEI'] = NEI + NI5 = NamedInt('test', 5) + self.assertEqual(NI5, 5) + self.assertEqual(NEI.y.value, 2) + test_pickle_dump_load(self.assertTrue, NEI.y) + test_pickle_dump_load(self.assertTrue, NEI) + + def test_tuple_subclass(self): + class SomeTuple(tuple, Enum): + __qualname__ = 'SomeTuple' + first = (1, 'for the money') + second = (2, 'for the show') + third = (3, 'for the music') + self.assertTrue(type(SomeTuple.first) is SomeTuple) + self.assertTrue(isinstance(SomeTuple.second, tuple)) + self.assertEqual(SomeTuple.third, (3, 'for the music')) + globals()['SomeTuple'] = SomeTuple + test_pickle_dump_load(self.assertTrue, SomeTuple.first) + + # def test_duplicate_values_give_unique_enum_items(self): + # class NumericEnum(AutoNumberEnum): + # __order__ = 'enum_m enum_d enum_y' + # enum_m = () + # enum_d = () + # enum_y = () + # def __int__(self): + # return int(self._value_) + # self.assertEqual(int(NumericEnum.enum_d), 2) + # self.assertEqual(NumericEnum.enum_y.value, 3) + # self.assertTrue(NumericEnum(1) is NumericEnum.enum_m) + # self.assertEqual( + # list(NumericEnum), + # [NumericEnum.enum_m, NumericEnum.enum_d, NumericEnum.enum_y], + # ) + + def test_inherited_new_from_enhanced_enum(self): + class AutoNumber2(Enum): + def __new__(cls): + value = len(cls.__members__) + 1 + obj = object.__new__(cls) + obj._value_ = value + return obj + def __int__(self): + return int(self._value_) + class Color(AutoNumber2): + __order__ = 'red green blue' + red = () + green = () + blue = () + self.assertEqual(len(Color), 3, "wrong number of elements: %d (should be %d)" % (len(Color), 3)) + self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) + if PY3: + self.assertEqual(list(map(int, Color)), [1, 2, 3]) + + def test_inherited_new_from_mixed_enum(self): + class AutoNumber3(IntEnum): + def __new__(cls): + value = len(cls.__members__) + 11 + obj = int.__new__(cls, value) + obj._value_ = value + return obj + class Color(AutoNumber3): + __order__ = 'red green blue' + red = () + green = () + blue = () + self.assertEqual(len(Color), 3, "wrong number of elements: %d (should be %d)" % (len(Color), 3)) + Color.red + Color.green + Color.blue + self.assertEqual(Color.blue, 13) + + def test_equality(self): + class AlwaysEqual: + def __eq__(self, other): + return True + class OrdinaryEnum(Enum): + a = 1 + self.assertEqual(AlwaysEqual(), OrdinaryEnum.a) + self.assertEqual(OrdinaryEnum.a, AlwaysEqual()) + + def test_ordered_mixin(self): + class Grade(OrderedEnum): + __order__ = 'A B C D F' + A = 5 + B = 4 + C = 3 + D = 2 + F = 1 + self.assertEqual(list(Grade), [Grade.A, Grade.B, Grade.C, Grade.D, Grade.F]) + self.assertTrue(Grade.A > Grade.B) + self.assertTrue(Grade.F <= Grade.C) + self.assertTrue(Grade.D < Grade.A) + self.assertTrue(Grade.B >= Grade.B) + + def test_missing_deprecated(self): + class Label(Enum): + AnyApple = 0 + RedApple = 1 + GreenApple = 2 + @classmethod + def _missing_(cls, name): + return cls.AnyApple + + self.assertEqual(Label.AnyApple, Label(4)) + with self.assertRaises(AttributeError): + Label.redapple + with self.assertRaises(KeyError): + Label['redapple'] + + def test_missing(self): + class Label(Enum): + AnyApple = 0 + RedApple = 1 + GreenApple = 2 + @classmethod + def _missing_value_(cls, value): + return cls.AnyApple + + self.assertEqual(Label.AnyApple, Label(4)) + with self.assertRaises(AttributeError): + Label.redapple + with self.assertRaises(KeyError): + Label['redapple'] + + def test_missing_name(self): + class Label(Enum): + RedApple = 1 + GreenApple = 2 + @classmethod + def _missing_name_(cls, name): + for member in cls: + if member.name.lower() == name.lower(): + return member + + Label['redapple'] + with self.assertRaises(AttributeError): + Label.redapple + with self.assertRaises(ValueError): + Label('redapple') + + def test_missing_value_bad_input(self): + class Label(Enum): + AnyApple = 0 + RedApple = 1 + GreenApple = 2 + @classmethod + def _missing_value_(cls, value): + return cls.AnyApple + + self.assertEqual(Label.AnyApple, Label(4)) + with self.assertRaises(KeyError): + Label[True] + + def test_missing_name_bad_return(self): + class Label(Enum): + RedApple = 1 + GreenApple = 2 + @classmethod + def _missing_name_(cls, name): + return None + + with self.assertRaises(AttributeError): + Label.redapple + with self.assertRaises(ValueError): + Label('redapple') + with self.assertRaises(KeyError): + Label['redapple'] + + def test_extending2(self): + def bad_extension(): + class Shade(Enum): + def shade(self): + print(self.name) + class Color(Shade): + red = 1 + green = 2 + blue = 3 + class MoreColor(Color): + cyan = 4 + magenta = 5 + yellow = 6 + self.assertRaises(TypeError, bad_extension) + + def test_extending3(self): + class Shade(Enum): + def shade(self): + return self.name + class Color(Shade): + def hex(self): + return '%s hexlified!' % self.value + class MoreColor(Color): + cyan = 4 + magenta = 5 + yellow = 6 + self.assertEqual(MoreColor.magenta.hex(), '5 hexlified!') + + def test_extending5(self): + class Color(Enum): + _order_ = 'red green blue value' + red = 1 + green = 2 + blue = 3 + value = 4 + self.assertEqual(list(Color), [Color.red, Color.green, Color.blue, Color.value]) + self.assertEqual(Color.value.name, 'value') + self.assertEqual(Color.value.value, 4) + self.assertTrue(Color.value in Color) + self.assertEqual(Color(4), Color.value) + self.assertEqual(Color['value'], Color.value) + self.assertEqual(Color.red.value, 1) + + CONTINUE = 100, 'Continue', 'Request received, please continue' + SWITCHING_PROTOCOLS = (101, 'Switching Protocols', + 'Switching to new protocol; obey Upgrade header') + PROCESSING = 102, 'Processing' + + def test_no_duplicates(self): + def bad_duplicates(): + class Color1(UniqueEnum): + red = 1 + green = 2 + blue = 3 + class Color2(UniqueEnum): + red = 1 + green = 2 + blue = 3 + grene = 2 + self.assertRaises(ValueError, bad_duplicates) + + def test_no_duplicates_kinda(self): + class Silly(UniqueEnum): + one = 1 + two = 'dos' + name = 3 + class Sillier(IntEnum, UniqueEnum): + single = 1 + name = 2 + triple = 3 + value = 4 + + def test_init(self): + class Planet(Enum): + MERCURY = (3.303e+23, 2.4397e6) + VENUS = (4.869e+24, 6.0518e6) + EARTH = (5.976e+24, 6.37814e6) + MARS = (6.421e+23, 3.3972e6) + JUPITER = (1.9e+27, 7.1492e7) + SATURN = (5.688e+26, 6.0268e7) + URANUS = (8.686e+25, 2.5559e7) + NEPTUNE = (1.024e+26, 2.4746e7) + def __init__(self, mass, radius): + self.mass = mass # in kilograms + self.radius = radius # in meters + @property + def surface_gravity(self): + # universal gravitational constant (m3 kg-1 s-2) + G = 6.67300E-11 + return G * self.mass / (self.radius * self.radius) + self.assertEqual(round(Planet.EARTH.surface_gravity, 2), 9.80) + self.assertEqual(Planet.EARTH.value, (5.976e+24, 6.37814e6)) + + def test_init_and_shadowing_attribute(self): + class SelectionEnum(str, Enum): + _init_ = 'db user' + def __new__(cls, *args, **kwds): + count = len(cls.__members__) + obj = str.__new__(cls, args[0]) + obj._count = count + obj._value_ = args + return obj + @staticmethod + def _generate_next_value_(name, start, count, values, *args, **kwds): + return (name, ) + args + class DeviceTypeSource(SelectionEnum): + _order_ = 'user system' + user = "User controlled" + system = "System controlled" + self.assertEqual(DeviceTypeSource.system.db, 'system') + self.assertEqual(DeviceTypeSource.system.user, 'System controlled') + self.assertEqual(DeviceTypeSource.user.db, 'user') + self.assertEqual(DeviceTypeSource.user.user, 'User controlled') + + def test_nonhash_value(self): + class AutoNumberInAList(Enum): + def __new__(cls): + value = [len(cls.__members__) + 1] + obj = object.__new__(cls) + obj._value_ = value + return obj + class ColorInAList(AutoNumberInAList): + __order__ = 'red green blue' + red = () + green = () + blue = () + self.assertEqual(list(ColorInAList), [ColorInAList.red, ColorInAList.green, ColorInAList.blue]) + self.assertEqual(ColorInAList.red.value, [1]) + self.assertEqual(ColorInAList([1]), ColorInAList.red) + + def test_number_reset_and_order_cleanup(self): + class Confused(Enum): + _order_ = 'ONE TWO THREE UNO DOS TRES FOUR' + ONE = auto() + TWO = auto() + THREE = auto() + UNO = 1 + DOS = auto() + TRES = auto() + FOUR = auto() + self.assertEqual(list(Confused), [Confused.ONE, Confused.TWO, Confused.THREE, Confused.FOUR]) + self.assertIs(Confused.TWO, Confused.DOS) + self.assertEqual(Confused.DOS._value_, 2) + self.assertEqual(Confused.TRES._value_, 3) + self.assertEqual(Confused.FOUR._value_, 4) + + def test_conflicting_types_resolved_in_new(self): + class LabelledIntEnum(int, Enum): + def __new__(cls, *args): + value, label = args + obj = int.__new__(cls, value) + obj.label = label + obj._value_ = value + return obj + + class LabelledList(LabelledIntEnum): + unprocessed = (1, "Unprocessed") + payment_complete = (2, "Payment Complete") + + self.assertEqual(LabelledList.unprocessed, 1) + self.assertEqual(LabelledList(1), LabelledList.unprocessed) + self.assertEqual(list(LabelledList), [LabelledList.unprocessed, LabelledList.payment_complete]) + + def test_auto_number(self): + class Color(Enum): + _order_ = 'red blue green' + red = auto() + blue = auto() + green = auto() + + self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) + self.assertEqual(Color.red.value, 1) + self.assertEqual(Color.blue.value, 2) + self.assertEqual(Color.green.value, 3) + + def test_auto_name(self): + class Color(Enum): + _order_ = 'red blue green' + def _generate_next_value_(name, start, count, last): + return name + red = auto() + blue = auto() + green = auto() + + self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) + self.assertEqual(Color.red.value, 'red') + self.assertEqual(Color.blue.value, 'blue') + self.assertEqual(Color.green.value, 'green') + + def test_auto_name_inherit(self): + class AutoNameEnum(Enum): + def _generate_next_value_(name, start, count, last): + return name + class Color(AutoNameEnum): + _order_ = 'red blue green' + red = auto() + blue = auto() + green = auto() + + self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) + self.assertEqual(Color.red.value, 'red') + self.assertEqual(Color.blue.value, 'blue') + self.assertEqual(Color.green.value, 'green') + + def test_auto_garbage(self): + class Color(Enum): + _order_ = 'red blue' + red = 'red' + blue = auto() + self.assertEqual(Color.blue.value, 1) + + def test_auto_garbage_corrected(self): + class Color(Enum): + _order_ = 'red blue green' + red = 'red' + blue = 2 + green = auto() + + self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) + self.assertEqual(Color.red.value, 'red') + self.assertEqual(Color.blue.value, 2) + self.assertEqual(Color.green.value, 3) + + def test_duplicate_auto(self): + # + class MoreDupes(Enum): + _order_ = 'A B C' + A = auto() + B = A, + C = auto() + self.assertEqual(list(MoreDupes), [MoreDupes.A, MoreDupes.B, MoreDupes.C]) + self.assertEqual([m.value for m in MoreDupes], [1, (1, ), 2]) + # + class Dupes(Enum): + _order_ = 'first second third' + first = primero = auto() + second = auto() + third = auto() + self.assertEqual([Dupes.first, Dupes.second, Dupes.third], list(Dupes)) + + def test_auto_value_with_auto(self): + + class SelectionEnum(Enum): + _init_ = 'db user' + def __new__(cls, *args, **kwds): + count = len(cls.__members__) + obj = object.__new__(cls) + obj._count = count + obj._value_ = args + obj.db, obj.user = args + return obj + @staticmethod + def _generate_next_value_(name, start, count, values, *args, **kwds): + return (name, ) + args + + class Test(SelectionEnum): + _order_ = 'this that' + this = auto('these') + that = auto('those') + + self.assertEqual(list(Test), [Test.this, Test.that]) + self.assertEqual(Test.this.name, 'this') + self.assertEqual(Test.this.value, ('this', 'these')) + self.assertEqual(Test.this.db, 'this') + self.assertEqual(Test.this.user, 'these') + self.assertEqual(Test.that.name, 'that') + self.assertEqual(Test.that.value, ('that', 'those')) + self.assertEqual(Test.that.db, 'that') + self.assertEqual(Test.that.user, 'those') + + def test_auto_value_with_autovalue(self): + + class SelectionEnum(Enum): + _init_ = 'db user' + def __new__(cls, *args, **kwds): + count = len(cls.__members__) + obj = object.__new__(cls) + obj._count = count + obj._value_ = args + return obj + @staticmethod + def _generate_next_value_(name, start, count, values, *args, **kwds): + return (name, ) + args + + class Test(SelectionEnum): + _order_ = 'this that' + this = 'these' + that = 'those' + + self.assertEqual(list(Test), [Test.this, Test.that]) + self.assertEqual(Test.this.name, 'this') + self.assertEqual(Test.this.value, ('this', 'these')) + self.assertEqual(Test.this.db, 'this') + self.assertEqual(Test.this.user, 'these') + self.assertEqual(Test.that.name, 'that') + self.assertEqual(Test.that.value, ('that', 'those')) + self.assertEqual(Test.that.db, 'that') + self.assertEqual(Test.that.user, 'those') + + def test_auto_and_kwds(self): + class Item(Enum): + _order_ = 'A B' + A = auto(size=100, req={'red': True}) + B = auto(size=200, req={'red': False}) + # + def __new__(cls, value, size, req): + obj = object.__new__(cls) + obj._value_ = value + obj.size = size + obj.req= req + return obj + self.assertEqual((Item.A.value, Item.A.size, Item.A.req), (1, 100, {'red': True})) + self.assertEqual((Item.B.value, Item.B.size, Item.B.req), (2, 200, {'red': False})) + + def test_empty_with_functional_api(self): + empty = aenum.IntEnum('Foo', {}) + self.assertEqual(len(empty), 0) + + def test_auto_init(self): + class Planet(Enum): + _init_ = 'mass radius' + MERCURY = (3.303e+23, 2.4397e6) + VENUS = (4.869e+24, 6.0518e6) + EARTH = (5.976e+24, 6.37814e6) + MARS = (6.421e+23, 3.3972e6) + JUPITER = (1.9e+27, 7.1492e7) + SATURN = (5.688e+26, 6.0268e7) + URANUS = (8.686e+25, 2.5559e7) + NEPTUNE = (1.024e+26, 2.4746e7) + @property + def surface_gravity(self): + # universal gravitational constant (m3 kg-1 s-2) + G = 6.67300E-11 + return G * self.mass / (self.radius * self.radius) + self.assertEqual(round(Planet.EARTH.surface_gravity, 2), 9.80) + self.assertEqual(Planet.EARTH.value, (5.976e+24, 6.37814e6)) + + def test_auto_init_with_value(self): + class Color(Enum): + _init_='value, rgb' + RED = 1, (1, 0, 0) + BLUE = 2, (0, 1, 0) + GREEN = 3, (0, 0, 1) + self.assertEqual(Color.RED.value, 1) + self.assertEqual(Color.BLUE.value, 2) + self.assertEqual(Color.GREEN.value, 3) + self.assertEqual(Color.RED.rgb, (1, 0, 0)) + self.assertEqual(Color.BLUE.rgb, (0, 1, 0)) + self.assertEqual(Color.GREEN.rgb, (0, 0, 1)) + + def test_noalias(self): + class Settings(Enum): + _settings_ = NoAlias + red = 1 + rojo = 1 + self.assertFalse(Settings.red is Settings.rojo) + self.assertRaises(TypeError, Settings, 1) + + def test_auto_and_init(self): + class Field(int, Enum): + _order_ = 'TYPE START' + _init_ = 'value __doc__' + TYPE = "Char, Date, Logical, etc." + START = "Field offset in record" + self.assertEqual(Field.TYPE, 1) + self.assertEqual(Field.START, 2) + self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.') + self.assertEqual(Field.START.__doc__, 'Field offset in record') + + def test_auto_and_start(self): + class Field(IntEnum): + _order_ = 'TYPE START' + _start_ = 0 + _init_ = 'value __doc__' + TYPE = "Char, Date, Logical, etc." + START = "Field offset in record" + self.assertEqual(Field.TYPE, 0) + self.assertEqual(Field.START, 1) + self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.') + self.assertEqual(Field.START.__doc__, 'Field offset in record') + + def test_auto_and_init_and_some_values(self): + class Field(int, Enum): + _order_ = 'TYPE START BLAH BELCH' + _init_ = 'value __doc__' + TYPE = "Char, Date, Logical, etc." + START = "Field offset in record" + BLAH = 5, "test blah" + BELCH = 'test belch' + self.assertEqual(Field.TYPE, 1) + self.assertEqual(Field.START, 2) + self.assertEqual(Field.BLAH, 5) + self.assertEqual(Field.BELCH, 6) + self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.') + self.assertEqual(Field.START.__doc__, 'Field offset in record') + self.assertEqual(Field.BLAH.__doc__, 'test blah') + self.assertEqual(Field.BELCH.__doc__, 'test belch') + + def test_auto_and_init_w_value_and_too_many_values(self): + with self.assertRaisesRegex(TypeError, r'Field\.BLAH: number of fields provided do not match init'): + class Field(int, Enum): + _order_ = 'TYPE START BLAH BELCH' + _init_ = 'value __doc__' + TYPE = 1, "Char, Date, Logical, etc." + START = 2, "Field offset in record" + BLAH = 5, 6, "test blah" + BELCH = 7, 'test belch' + + def test_auto_and_init_and_some_complex_values(self): + class Field(int, Enum): + _order_ = 'TYPE START BLAH BELCH' + _init_ = 'value __doc__ help' + TYPE = "Char, Date, Logical, etc.", "fields composed of character data" + START = "Field offset in record", "where the data starts in the record" + BLAH = 5, "test blah", "some help" + BELCH = 'test belch', "some more help" + self.assertEqual(Field.TYPE, 1) + self.assertEqual(Field.START, 2) + self.assertEqual(Field.BLAH, 5) + self.assertEqual(Field.BELCH, 6) + self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.') + self.assertEqual(Field.START.__doc__, 'Field offset in record') + self.assertEqual(Field.BLAH.__doc__, 'test blah') + self.assertEqual(Field.BELCH.__doc__, 'test belch') + self.assertEqual(Field.TYPE.help, "fields composed of character data") + self.assertEqual(Field.START.help, "where the data starts in the record") + self.assertEqual(Field.BLAH.help, "some help") + self.assertEqual(Field.BELCH.help, "some more help") + + def test_auto_and_init_inherited(self): + class AutoEnum(IntEnum): + _start_ = 0 + _init_ = 'value __doc__' + class Field(AutoEnum): + _order_ = 'TYPE START BLAH BELCH' + TYPE = "Char, Date, Logical, etc." + START = "Field offset in record" + BLAH = 5, "test blah" + BELCH = 'test belch' + self.assertEqual(Field.TYPE, 0) + self.assertEqual(Field.START, 1) + self.assertEqual(Field.BLAH, 5) + self.assertEqual(Field.BELCH, 6) + self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.') + self.assertEqual(Field.START.__doc__, 'Field offset in record') + self.assertEqual(Field.BLAH.__doc__, 'test blah') + self.assertEqual(Field.BELCH.__doc__, 'test belch') + + def test_missing_value_error(self): + with self.assertRaisesRegex(TypeError, r"_value_ not set in __new__"): + class Combined(str, Enum): + # + _init_ = 'value sequence' + _order_ = lambda m: m.sequence + # + def __new__(cls, value, *args): + enum = str.__new__(cls, value) + if '(' in value: + fis_name, segment = value.split('(', 1) + segment = segment.strip(' )') + else: + fis_name = value + segment = None + enum.fis_name = fis_name + enum.segment = segment + return enum + # + def __repr__(self): + return "<%s.%s>" % (self.__class__.__name__, self._name_) + # + key_type = 'An$(1,2)', 0 + company_id = 'An$(3,2)', 1 + code = 'An$(5,1)', 2 + description = 'Bn$', 3 + + + def test_auto_and_enum(self): + class Foo(aenum.Flag): + _order_ = 'a b c' + a = aenum.auto() + b = a | aenum.auto() + c = 2 + + self.assertEqual([Foo.a, Foo.c], list(Foo)) + self.assertEqual(Foo.a.value, 1) + self.assertEqual(Foo.b.value, 3) + + def test_multiple_arg_auto(self): + class AutoName(Enum): + def _generate_next_value_(name, start, count, last, *args, **kwds): + return (name, ) + args + # + class Planet(AutoName): + _init_ = 'value mass radius' + MERCURY = auto(3.303e+23, 2.4397e6) + VENUS = auto(4.869e+24, 6.0518e6) + self.assertEqual(Planet.MERCURY.value, 'MERCURY') + + def test_auto_w_multiple_arg(self): + class AutoName(Enum): + def _generate_next_value_(name, start, count, last, *args, **kwds): + return (name, ) + args + # + class Planet(AutoName): + _init_ = 'value mass radius' + MERCURY = auto(), 3.303e+23, 2.4397e6 # doesn't work + VENUS = auto(), 4.869e+24, 6.0518e6 # doesn't work + self.assertEqual(Planet.MERCURY.value, 'MERCURY') + + def test_auto_gnv_and_init(self): + class AutoName(Enum): + def _generate_next_value_(name, start, count, last, *args, **kwds): + return (name, ) + args + # + class Planet(AutoName): + _init_ = 'value mass radius' + MERCURY = 3.303e+23, 2.4397e6 # doesn't work + VENUS = 4.869e+24, 6.0518e6 # doesn't work + self.assertEqual(Planet.MERCURY.value, 'MERCURY') + + # def test_AutoNumberEnum_and_property(self): + # class Color(aenum.AutoNumberEnum): + # red = () + # green = () + # blue = () + # @property + # def cap_name(self): + # return self.name.title() + # self.assertEqual(Color.blue.cap_name, 'Blue') + + # def test_AutoNumberEnum(self): + # class Color(aenum.AutoNumberEnum): + # _order_ = 'red green blue' + # red = () + # green = () + # blue = () + # self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) + # self.assertEqual(Color.red.value, 1) + # self.assertEqual(Color.green.value, 2) + # self.assertEqual(Color.blue.value, 3) + + def test_MultiValue_with_init_wo_value(self): + class Color(Enum): + _init_ = 'color r g b' + _order_ = 'red green blue' + _settings_ = MultiValue + red = 'red', 1, 2, 3 + green = 'green', 4, 5, 6 + blue = 'blue', 7, 8, 9 + self.assertEqual(Color.red.value, 'red') + self.assertEqual(Color.red.color, 'red') + self.assertEqual(Color.red.r, 1) + self.assertEqual(Color.red.g, 2) + self.assertEqual(Color.red.b, 3) + self.assertEqual(Color.green.value, 'green') + self.assertEqual(Color.green.color, 'green') + self.assertEqual(Color.green.r, 4) + self.assertEqual(Color.green.g, 5) + self.assertEqual(Color.green.b, 6) + self.assertEqual(Color.blue.value, 'blue') + self.assertEqual(Color.blue.color, 'blue') + self.assertEqual(Color.blue.r, 7) + self.assertEqual(Color.blue.g, 8) + self.assertEqual(Color.blue.b, 9) + self.assertIs(Color('red'), Color.red) + self.assertIs(Color(1), Color.red) + self.assertIs(Color(2), Color.red) + self.assertIs(Color(3), Color.red) + self.assertIs(Color('green'), Color.green) + self.assertIs(Color(4), Color.green) + self.assertIs(Color(5), Color.green) + self.assertIs(Color(6), Color.green) + self.assertIs(Color('blue'), Color.blue) + self.assertIs(Color(7), Color.blue) + self.assertIs(Color(8), Color.blue) + self.assertIs(Color(9), Color.blue) + + def test_MultiValue_with_init_w_value(self): + class Color(Enum): + _init_ = 'value r g b' + _order_ = 'red green blue' + _settings_ = MultiValue + red = 'red', 1, 2, 3 + green = 'green', 4, 5, 6 + blue = 'blue', 7, 8, 9 + self.assertEqual(Color.red.value, 'red') + self.assertEqual(Color.red.r, 1) + self.assertEqual(Color.red.g, 2) + self.assertEqual(Color.red.b, 3) + self.assertEqual(Color.green.value, 'green') + self.assertEqual(Color.green.r, 4) + self.assertEqual(Color.green.g, 5) + self.assertEqual(Color.green.b, 6) + self.assertEqual(Color.blue.value, 'blue') + self.assertEqual(Color.blue.r, 7) + self.assertEqual(Color.blue.g, 8) + self.assertEqual(Color.blue.b, 9) + self.assertIs(Color('red'), Color.red) + self.assertIs(Color(1), Color.red) + self.assertIs(Color(2), Color.red) + self.assertIs(Color(3), Color.red) + self.assertIs(Color('green'), Color.green) + self.assertIs(Color(4), Color.green) + self.assertIs(Color(5), Color.green) + self.assertIs(Color(6), Color.green) + self.assertIs(Color('blue'), Color.blue) + self.assertIs(Color(7), Color.blue) + self.assertIs(Color(8), Color.blue) + self.assertIs(Color(9), Color.blue) + + def test_MultiValue_with_init_wo_value_w_autonumber(self): + class Color(AutoNumberEnum): + _init_ = 'color r g b' + _order_ = 'red green blue' + _settings_ = MultiValue + red = 'red', 10, 20, 30 + green = 'green', 40, 50, 60 + blue = 'blue', 70, 80, 90 + self.assertEqual(Color.red.value, 1) + self.assertEqual(Color.red.color, 'red') + self.assertEqual(Color.red.r, 10) + self.assertEqual(Color.red.g, 20) + self.assertEqual(Color.red.b, 30) + self.assertEqual(Color.green.value, 2) + self.assertEqual(Color.green.color, 'green') + self.assertEqual(Color.green.r, 40) + self.assertEqual(Color.green.g, 50) + self.assertEqual(Color.green.b, 60) + self.assertEqual(Color.blue.value, 3) + self.assertEqual(Color.blue.color, 'blue') + self.assertEqual(Color.blue.r, 70) + self.assertEqual(Color.blue.g, 80) + self.assertEqual(Color.blue.b, 90) + self.assertIs(Color(1), Color.red) + self.assertIs(Color('red'), Color.red) + self.assertIs(Color(10), Color.red) + self.assertIs(Color(20), Color.red) + self.assertIs(Color(30), Color.red) + self.assertIs(Color(2), Color.green) + self.assertIs(Color('green'), Color.green) + self.assertIs(Color(40), Color.green) + self.assertIs(Color(50), Color.green) + self.assertIs(Color(60), Color.green) + self.assertIs(Color(3), Color.blue) + self.assertIs(Color('blue'), Color.blue) + self.assertIs(Color(70), Color.blue) + self.assertIs(Color(80), Color.blue) + self.assertIs(Color(90), Color.blue) + + def test_multivalue_and_autonumber_wo_init_wo_value(self): + class Day(Enum): + _settings_ = MultiValue, AddValue + _order_ = 'one two three' + _start_ = 7 + one = "21", "one" + two = "22", "two" + three = "23", "three" + self.assertEqual(Day.one.value, 7) + self.assertEqual(Day.two.value, 8) + self.assertEqual(Day.three.value, 9) + self.assertEqual(Day('21'), Day.one) + self.assertEqual(Day('one'), Day.one) + + def test_multivalue_and_autonumber_wo_init_w_some_value(self): + class Color(Enum): + _settings_ = MultiValue, Unique + _order_ = 'BLACK RED BLUE YELLOW GREEN MAGENTA' + _init_ = "value description" + BLACK = -1, "Text0" + RED = -50, "Text1" + BLUE = auto(), "Text2" + YELLOW = auto(), "Text3" + GREEN = -70, "Text4" + MAGENTA = auto(), "Text5" + self.assertEqual(Color.BLACK.value, -1) + self.assertEqual(Color.RED.value, -50) + self.assertEqual(Color.BLUE.value, -49) + self.assertEqual(Color.YELLOW.value, -48) + self.assertEqual(Color.GREEN.value, -70) + self.assertEqual(Color.MAGENTA.value, -69) + self.assertEqual(Color(-1), Color.BLACK) + self.assertEqual(Color('Text2'), Color.BLUE) + + def test_combine_new_settings_with_old_settings(self): + class Auto(Enum): + _settings_ = Unique + with self.assertRaises(ValueError): + class AutoUnique(Auto): + BLAH = auto() + BLUH = auto() + ICK = 1 + + def test_timedelta(self): + class Period(timedelta, Enum): + ''' + different lengths of time + ''' + _init_ = 'value period' + _settings_ = NoAlias + _ignore_ = 'Period i' + Period = vars() + for i in range(31): + Period['day_%d' % i] = i, 'day' + for i in range(15): + Period['week_%d' % i] = i*7, 'week' + for i in range(12): + Period['month_%d' % i] = i*30, 'month' + OneDay = day_1 + OneWeek = week_1 + self.assertFalse(hasattr(Period, '_ignore_')) + self.assertFalse(hasattr(Period, 'Period')) + self.assertFalse(hasattr(Period, 'i')) + self.assertTrue(isinstance(Period.day_1, timedelta)) + + def test_skip(self): + class enumA(Enum): + @skip + class enumB(Enum): + elementA = 'a' + elementB = 'b' + @skip + class enumC(Enum): + elementC = 'c' + elementD = 'd' + self.assertIs(enumA.enumB, enumA.__dict__['enumB']) + + def test_nonmember(self): + class enumA(Enum): + @nonmember + class enumB(Enum): + elementA = 'a' + elementB = 'b' + @nonmember + class enumC(Enum): + elementC = 'c' + elementD = 'd' + self.assertIs(enumA.enumB, enumA.__dict__['enumB']) + + def test_member_with_external_functions(self): + class Func(Enum): + _order_ = 'an_int a_str' + an_int = member(int) + a_str = member(str) + @classproperty + def types(cls): + return [m.value for m in list(cls)] + def __repr__(self): + return "<%s.%s>" % (self.__class__.__name__, self.name, ) + def __call__(self, *args, **kwds): + return self.value(*args, **kwds) + # + self.assertEqual([Func.an_int, Func.a_str], list(Func)) + self.assertEqual([int, str], Func.types) + self.assertEqual(Func.an_int(7), 7) + self.assertEqual(Func.a_str('BlahBlah'), 'BlahBlah') + + def test_member_with_internal_functions(self): + class Func(Enum): + _order_ = 'haha hehe' + @member + def haha(): + return 'haha' + @member + def hehe(name): + return 'hehe -- what a name! %s!' % name + @classproperty + def types(cls): + return [m.value for m in list(cls)] + def __repr__(self): + return "<%s.%s>" % (self.__class__.__name__, self.name, ) + def __call__(self, *args, **kwds): + return self.value(*args, **kwds) + # + self.assertEqual([Func.haha, Func.hehe], list(Func)) + self.assertEqual([Func.haha.value, Func.hehe.value], Func.types) + self.assertEqual(Func.haha(), 'haha') + self.assertEqual(Func.hehe('BlahBlah'), 'hehe -- what a name! BlahBlah!') + + def test_constantness_of_constants(self): + class Universe(Enum): + PI = constant(3.141596) + G = constant(6.67300E-11) + self.assertEqual(Universe.PI, 3.141596) + self.assertRaisesRegex(AttributeError, r'cannot rebind constant', setattr, Universe, 'PI', 9) + self.assertRaisesRegex(AttributeError, r'cannot delete constant', delattr, Universe, 'PI') + + def test_math_and_stuff_with_constants(self): + class Universe(Enum): + PI = constant(3.141596) + TAU = constant(2 * PI) + self.assertEqual(Universe.PI, 3.141596) + self.assertEqual(Universe.TAU, 2 * Universe.PI) + + def test_constant_with_auto_is_updated(self): + class Fruit(Flag): + _order_ = 'apple banana lemon orange' + apple = auto() + banana = auto() + lemon = auto() + orange = auto() + CitrusTypes = constant(lemon | orange) + self.assertEqual(list(Fruit), [Fruit.apple, Fruit.banana, Fruit.lemon, Fruit.orange]) + self.assertEqual(list(Fruit.CitrusTypes), [Fruit.lemon, Fruit.orange]) + self.assertTrue(Fruit.orange in Fruit.CitrusTypes) + + + def test_order_as_function(self): + # first with _init_ + class TestSequence(Enum): + _init_ = 'value, sequence' + _order_ = lambda member: member.sequence + item_id = 'An$(1,6)', 0 # Item Code + company_id = 'An$(7,2)', 1 # Company Code + warehouse_no = 'An$(9,4)', 2 # Warehouse Number + company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY + key_type = 'Cn$(19,3)', 4 # Key Type = '1**' + available = 'Zn$(1,1)', 5 # Available? + contract_item = 'Bn(2,1)', 6 # Contract Item? + sales_category = 'Fn', 7 # Sales Category + gl_category = 'Rn$(5,1)', 8 # G/L Category + warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category + inv_units = 'Qn$(7,2)', 10 # Inv Units + for i, member in enumerate(TestSequence): + self.assertEqual(i, member.sequence) + ts = TestSequence + self.assertEqual(ts.item_id.name, 'item_id') + self.assertEqual(ts.item_id.value, 'An$(1,6)') + self.assertEqual(ts.item_id.sequence, 0) + self.assertEqual(ts.company_id.name, 'company_id') + self.assertEqual(ts.company_id.value, 'An$(7,2)') + self.assertEqual(ts.company_id.sequence, 1) + self.assertEqual(ts.warehouse_no.name, 'warehouse_no') + self.assertEqual(ts.warehouse_no.value, 'An$(9,4)') + self.assertEqual(ts.warehouse_no.sequence, 2) + self.assertEqual(ts.company.name, 'company') + self.assertEqual(ts.company.value, 'Hn$(13,6)') + self.assertEqual(ts.company.sequence, 3) + self.assertEqual(ts.key_type.name, 'key_type') + self.assertEqual(ts.key_type.value, 'Cn$(19,3)') + self.assertEqual(ts.key_type.sequence, 4) + self.assertEqual(ts.available.name, 'available') + self.assertEqual(ts.available.value, 'Zn$(1,1)') + self.assertEqual(ts.available.sequence, 5) + self.assertEqual(ts.contract_item.name, 'contract_item') + self.assertEqual(ts.contract_item.value, 'Bn(2,1)') + self.assertEqual(ts.contract_item.sequence, 6) + self.assertEqual(ts.sales_category.name, 'sales_category') + self.assertEqual(ts.sales_category.value, 'Fn') + self.assertEqual(ts.sales_category.sequence, 7) + self.assertEqual(ts.gl_category.name, 'gl_category') + self.assertEqual(ts.gl_category.value, 'Rn$(5,1)') + self.assertEqual(ts.gl_category.sequence, 8) + self.assertEqual(ts.warehouse_category.name, 'warehouse_category') + self.assertEqual(ts.warehouse_category.value, 'Sn$(6,1)') + self.assertEqual(ts.warehouse_category.sequence, 9) + self.assertEqual(ts.inv_units.name, 'inv_units') + self.assertEqual(ts.inv_units.value, 'Qn$(7,2)') + self.assertEqual(ts.inv_units.sequence, 10) + # and then without + class TestSequence(Enum): + _order_ = lambda member: member.value[1] + item_id = 'An$(1,6)', 0 # Item Code + company_id = 'An$(7,2)', 1 # Company Code + warehouse_no = 'An$(9,4)', 2 # Warehouse Number + company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY + key_type = 'Cn$(19,3)', 4 # Key Type = '1**' + available = 'Zn$(1,1)', 5 # Available? + contract_item = 'Bn(2,1)', 6 # Contract Item? + sales_category = 'Fn', 7 # Sales Category + gl_category = 'Rn$(5,1)', 8 # G/L Category + warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category + inv_units = 'Qn$(7,2)', 10 # Inv Units + for i, member in enumerate(TestSequence): + self.assertEqual(i, member.value[1]) + ts = TestSequence + self.assertEqual(ts.item_id.name, 'item_id') + self.assertEqual(ts.item_id.value, ('An$(1,6)', 0)) + self.assertEqual(ts.company_id.name, 'company_id') + self.assertEqual(ts.company_id.value, ('An$(7,2)', 1)) + self.assertEqual(ts.warehouse_no.name, 'warehouse_no') + self.assertEqual(ts.warehouse_no.value, ('An$(9,4)', 2)) + self.assertEqual(ts.company.name, 'company') + self.assertEqual(ts.company.value, ('Hn$(13,6)', 3)) + self.assertEqual(ts.key_type.name, 'key_type') + self.assertEqual(ts.key_type.value, ('Cn$(19,3)', 4)) + self.assertEqual(ts.available.name, 'available') + self.assertEqual(ts.available.value, ('Zn$(1,1)', 5)) + self.assertEqual(ts.contract_item.name, 'contract_item') + self.assertEqual(ts.contract_item.value, ('Bn(2,1)', 6)) + self.assertEqual(ts.sales_category.name, 'sales_category') + self.assertEqual(ts.sales_category.value, ('Fn', 7)) + self.assertEqual(ts.gl_category.name, 'gl_category') + self.assertEqual(ts.gl_category.value, ('Rn$(5,1)', 8)) + self.assertEqual(ts.warehouse_category.name, 'warehouse_category') + self.assertEqual(ts.warehouse_category.value, ('Sn$(6,1)', 9)) + self.assertEqual(ts.inv_units.name, 'inv_units') + self.assertEqual(ts.inv_units.value, ('Qn$(7,2)', 10)) + # then with _init_ but without value + with self.assertRaises(TypeError): + class TestSequence(Enum): + _init_ = 'sequence' + _order_ = lambda member: member.sequence + item_id = 'An$(1,6)', 0 # Item Code + company_id = 'An$(7,2)', 1 # Company Code + warehouse_no = 'An$(9,4)', 2 # Warehouse Number + company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY + key_type = 'Cn$(19,3)', 4 # Key Type = '1**' + available = 'Zn$(1,1)', 5 # Available? + contract_item = 'Bn(2,1)', 6 # Contract Item? + sales_category = 'Fn', 7 # Sales Category + gl_category = 'Rn$(5,1)', 8 # G/L Category + warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category + inv_units = 'Qn$(7,2)', 10 # Inv Units + # finally, out of order so Python 3 barfs + with self.assertRaises(TypeError): + class TestSequence(Enum): + _init_ = 'sequence' + _order_ = lambda member: member.sequence + item_id = 'An$(1,6)', 0 # Item Code + warehouse_no = 'An$(9,4)', 2 # Warehouse Number + company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY + company_id = 'An$(7,2)', 1 # Company Code + inv_units = 'Qn$(7,2)', 10 # Inv Units + available = 'Zn$(1,1)', 5 # Available? + contract_item = 'Bn(2,1)', 6 # Contract Item? + sales_category = 'Fn', 7 # Sales Category + key_type = 'Cn$(19,3)', 4 # Key Type = '1**' + gl_category = 'Rn$(5,1)', 8 # G/L Category + warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category + + def test_order_as_function_in_subclass(self): + # + class Parent(Enum): + _init_ = 'value sequence' + _order_ = lambda m: m.sequence + # + class Child(Parent): + item_id = 'An$(1,6)', 0 # Item Code + company_id = 'An$(7,2)', 1 # Company Code + warehouse_no = 'An$(9,4)', 2 # Warehouse Number + company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY + key_type = 'Cn$(19,3)', 4 # Key Type = '1**' + available = 'Zn$(1,1)', 5 # Available? + contract_item = 'Bn(2,1)', 6 # Contract Item? + sales_category = 'Fn', 7 # Sales Category + gl_category = 'Rn$(5,1)', 8 # G/L Category + warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category + inv_units = 'Qn$(7,2)', 10 # Inv Units + # + for i, member in enumerate(Child): + self.assertEqual(i, member.sequence) + # + ts = Child + self.assertEqual(ts.item_id.name, 'item_id') + self.assertEqual(ts.item_id.value, 'An$(1,6)') + self.assertEqual(ts.item_id.sequence, 0) + self.assertEqual(ts.company_id.name, 'company_id') + self.assertEqual(ts.company_id.value, 'An$(7,2)') + self.assertEqual(ts.company_id.sequence, 1) + self.assertEqual(ts.warehouse_no.name, 'warehouse_no') + self.assertEqual(ts.warehouse_no.value, 'An$(9,4)') + self.assertEqual(ts.warehouse_no.sequence, 2) + self.assertEqual(ts.company.name, 'company') + self.assertEqual(ts.company.value, 'Hn$(13,6)') + self.assertEqual(ts.company.sequence, 3) + self.assertEqual(ts.key_type.name, 'key_type') + self.assertEqual(ts.key_type.value, 'Cn$(19,3)') + self.assertEqual(ts.key_type.sequence, 4) + self.assertEqual(ts.available.name, 'available') + self.assertEqual(ts.available.value, 'Zn$(1,1)') + self.assertEqual(ts.available.sequence, 5) + self.assertEqual(ts.contract_item.name, 'contract_item') + self.assertEqual(ts.contract_item.value, 'Bn(2,1)') + self.assertEqual(ts.contract_item.sequence, 6) + self.assertEqual(ts.sales_category.name, 'sales_category') + self.assertEqual(ts.sales_category.value, 'Fn') + self.assertEqual(ts.sales_category.sequence, 7) + self.assertEqual(ts.gl_category.name, 'gl_category') + self.assertEqual(ts.gl_category.value, 'Rn$(5,1)') + self.assertEqual(ts.gl_category.sequence, 8) + self.assertEqual(ts.warehouse_category.name, 'warehouse_category') + self.assertEqual(ts.warehouse_category.value, 'Sn$(6,1)') + self.assertEqual(ts.warehouse_category.sequence, 9) + self.assertEqual(ts.inv_units.name, 'inv_units') + self.assertEqual(ts.inv_units.value, 'Qn$(7,2)') + self.assertEqual(ts.inv_units.sequence, 10) + + pass + + def test_multiple_mixin(self): + class MaxMixin(object): + @classproperty + def MAX(cls): + max = len(cls) + cls.MAX = max + return max + class StrMixin(object): + def __str__(self): + return self._name_.lower() + class SomeEnum(Enum): + def behavior(self): + return 'booyah' + class AnotherEnum(Enum): + def behavior(self): + return 'nuhuh!' + def social(self): + return "what's up?" + class Color(MaxMixin, Enum): + _order_ = 'RED GREEN BLUE' + RED = auto() + GREEN = auto() + BLUE = auto() + self.assertEqual(Color.RED.value, 1) + self.assertEqual(Color.GREEN.value, 2) + self.assertEqual(Color.BLUE.value, 3) + self.assertEqual(Color.MAX, 3) + self.assertEqual(str(Color.BLUE), 'Color.BLUE') + class Color(MaxMixin, StrMixin, Enum): + _order_ = 'RED GREEN BLUE' + RED = auto() + GREEN = auto() + BLUE = auto() + self.assertEqual(Color.RED.value, 1) + self.assertEqual(Color.GREEN.value, 2) + self.assertEqual(Color.BLUE.value, 3) + self.assertEqual(Color.MAX, 3) + self.assertEqual(str(Color.BLUE), 'blue', '%r is not %r' % (str(Color.BLUE), 'blue')) + class Color(StrMixin, MaxMixin, Enum): + _order_ = 'RED GREEN BLUE' + RED = auto() + GREEN = auto() + BLUE = auto() + self.assertEqual(Color.RED.value, 1) + self.assertEqual(Color.GREEN.value, 2) + self.assertEqual(Color.BLUE.value, 3) + self.assertEqual(Color.MAX, 3) + self.assertEqual(str(Color.BLUE), 'blue', '%r is not %r' % (str(Color.BLUE), 'blue')) + class CoolColor(StrMixin, SomeEnum, Enum): + _order_ = 'RED GREEN BLUE' + RED = auto() + GREEN = auto() + BLUE = auto() + self.assertEqual(CoolColor.RED.value, 1) + self.assertEqual(CoolColor.GREEN.value, 2) + self.assertEqual(CoolColor.BLUE.value, 3) + self.assertEqual(str(CoolColor.BLUE), 'blue', '%r is not %r' % (str(Color.BLUE), 'blue')) + self.assertEqual(CoolColor.RED.behavior(), 'booyah') + class CoolerColor(StrMixin, AnotherEnum, Enum): + _order_ = 'RED GREEN BLUE' + RED = auto() + GREEN = auto() + BLUE = auto() + self.assertEqual(CoolerColor.RED.value, 1) + self.assertEqual(CoolerColor.GREEN.value, 2) + self.assertEqual(CoolerColor.BLUE.value, 3) + self.assertEqual(str(CoolerColor.BLUE), 'blue', '%r is not %r' % (str(Color.BLUE), 'blue')) + self.assertEqual(CoolerColor.RED.behavior(), 'nuhuh!') + self.assertEqual(CoolerColor.RED.social(), "what's up?") + class CoolestColor(StrMixin, SomeEnum, AnotherEnum): + _order_ = 'RED GREEN BLUE' + RED = auto() + GREEN = auto() + BLUE = auto() + self.assertEqual(CoolestColor.RED.value, 1) + self.assertEqual(CoolestColor.GREEN.value, 2) + self.assertEqual(CoolestColor.BLUE.value, 3) + self.assertEqual(str(CoolestColor.BLUE), 'blue', '%r is not %r' % (str(Color.BLUE), 'blue')) + self.assertEqual(CoolestColor.RED.behavior(), 'booyah') + self.assertEqual(CoolestColor.RED.social(), "what's up?") + class ConfusedColor(StrMixin, AnotherEnum, SomeEnum): + _order_ = 'RED GREEN BLUE' + RED = auto() + GREEN = auto() + BLUE = auto() + self.assertEqual(ConfusedColor.RED.value, 1) + self.assertEqual(ConfusedColor.GREEN.value, 2) + self.assertEqual(ConfusedColor.BLUE.value, 3) + self.assertEqual(str(ConfusedColor.BLUE), 'blue', '%r is not %r' % (str(Color.BLUE), 'blue')) + self.assertEqual(ConfusedColor.RED.behavior(), 'nuhuh!') + self.assertEqual(ConfusedColor.RED.social(), "what's up?") + class ReformedColor(StrMixin, IntEnum, SomeEnum, AnotherEnum): + _order_ = 'RED GREEN BLUE' + RED = auto() + GREEN = auto() + BLUE = auto() + self.assertEqual(ReformedColor.RED.value, 1) + self.assertEqual(ReformedColor.GREEN.value, 2) + self.assertEqual(ReformedColor.BLUE.value, 3) + self.assertEqual(str(ReformedColor.BLUE), 'blue', '%r is not %r' % (str(Color.BLUE), 'blue')) + self.assertEqual(ReformedColor.RED.behavior(), 'booyah') + self.assertEqual(ConfusedColor.RED.social(), "what's up?") + self.assertTrue(issubclass(ReformedColor, int)) + + def test_multiple_inherited_mixin(self): + @unique + class Decision1(StrEnum): + REVERT = "REVERT" + REVERT_ALL = "REVERT_ALL" + RETRY = "RETRY" + class MyEnum(StrEnum): + pass + @unique + class Decision2(MyEnum): + REVERT = "REVERT" + REVERT_ALL = "REVERT_ALL" + RETRY = "RETRY" + + def test_value_auto_assign(self): + class Some(Enum): + def __new__(cls, val): + return object.__new__(cls) + x = 1 + y = 2 + self.assertEqual(Some.x.value, 1) + self.assertEqual(Some.y.value, 2) + + def test_enum_of_types(self): + """Support using Enum to refer to types deliberately.""" + class MyTypes(Enum): + i = int + f = float + s = str + self.assertEqual(MyTypes.i.value, int) + self.assertEqual(MyTypes.f.value, float) + self.assertEqual(MyTypes.s.value, str) + class Foo: + pass + class Bar: + pass + class MyTypes2(Enum): + a = Foo + b = Bar + self.assertEqual(MyTypes2.a.value, Foo) + self.assertEqual(MyTypes2.b.value, Bar) + class SpamEnumNotInner: + pass + class SpamEnum(Enum): + spam = SpamEnumNotInner + self.assertEqual(SpamEnum.spam.value, SpamEnumNotInner) + + if PY2: + def test_nested_classes_in_enum_do_become_members(self): + # manually set __qualname__ to remove testing framework noise + class Outer(Enum): + _order_ = 'a b Inner' + __qualname__ = "Outer" + a = 1 + b = 2 + class Inner(Enum): + __qualname__ = "Outer.Inner" + foo = 10 + bar = 11 + self.assertTrue(isinstance(Outer.Inner, Outer)) + self.assertEqual(Outer.a.value, 1) + self.assertEqual(Outer.Inner.value.foo.value, 10) + self.assertEqual( + list(Outer.Inner.value), + [Outer.Inner.value.foo, Outer.Inner.value.bar], + ) + self.assertEqual( + list(Outer), + [Outer.a, Outer.b, Outer.Inner], + ) + + def test_really_nested_classes_in_enum_do_become_members(self): + class Outer(Enum): + _order_ = 'a b Inner' + a = 1 + b = 2 + class Inner(Enum): + foo = 10 + bar = 11 + self.assertTrue(isinstance(Outer.Inner, Outer)) + self.assertEqual(Outer.a.value, 1) + self.assertEqual(Outer.Inner.value.foo.value, 10) + self.assertEqual( + list(Outer.Inner.value), + [Outer.Inner.value.foo, Outer.Inner.value.bar], + ) + self.assertEqual( + list(Outer), + [Outer.a, Outer.b, Outer.Inner], + ) + + def test_nested_classes_in_enum_are_skipped_with_skip(self): + """Support locally-defined nested classes using @skip""" + # manually set __qualname__ to remove testing framework noise + class Outer(Enum): + __qualname__ = "Outer" + a = 1 + b = 2 + @skip + class Inner(Enum): + __qualname__ = "Outer.Inner" + foo = 10 + bar = 11 + self.assertTrue(isinstance(Outer.Inner, type)) + self.assertEqual(Outer.a.value, 1) + self.assertEqual(Outer.Inner.foo.value, 10) + self.assertEqual( + list(Outer.Inner), + [Outer.Inner.foo, Outer.Inner.bar], + ) + self.assertEqual( + list(Outer), + [Outer.a, Outer.b], + ) + + def test_really_nested_classes_in_enum_are_skipped_with_skip(self): + """Support locally-defined nested classes using @skip""" + class Outer(Enum): + a = 1 + b = 2 + @skip + class Inner(Enum): + foo = 10 + bar = 11 + self.assertTrue(isinstance(Outer.Inner, type)) + self.assertEqual(Outer.a.value, 1) + self.assertEqual(Outer.Inner.foo.value, 10) + self.assertEqual( + list(Outer.Inner), + [Outer.Inner.foo, Outer.Inner.bar], + ) + self.assertEqual( + list(Outer), + [Outer.a, Outer.b], + ) + + def test_enum_call_without_arg(self): + class Color(Enum): + black = 0 + red = 1 + green = 2 + blue = 3 + # + @classmethod + def _missing_value_(cls, value): + if value is no_arg: + return cls.black + self.assertTrue(Color.red is Color(1)) + self.assertTrue(Color.black is Color()) + + def test_init_subclass(self): + class MyEnum(Enum): + def __init_subclass__(cls, **kwds): + super(MyEnum, cls).__init_subclass__(**kwds) + self.assertFalse(cls.__dict__.get('_test', False)) + cls._test1 = 'MyEnum' + # + class TheirEnum(MyEnum): + def __init_subclass__(cls, **kwds): + super(TheirEnum, cls).__init_subclass__(**kwds) + cls._test2 = 'TheirEnum' + class WhoseEnum(TheirEnum): + def __init_subclass__(cls, **kwds): + pass + class NoEnum(WhoseEnum): + ONE = 1 + self.assertEqual(TheirEnum.__dict__['_test1'], 'MyEnum') + self.assertEqual(WhoseEnum.__dict__['_test1'], 'MyEnum') + self.assertEqual(WhoseEnum.__dict__['_test2'], 'TheirEnum') + self.assertFalse(NoEnum.__dict__.get('_test1', False)) + self.assertFalse(NoEnum.__dict__.get('_test2', False)) + # + class OurEnum(MyEnum): + def __init_subclass__(cls, **kwds): + cls._test2 = 'OurEnum' + class WhereEnum(OurEnum): + def __init_subclass__(cls, **kwds): + pass + class NeverEnum(WhereEnum): + ONE = 'one' + self.assertEqual(OurEnum.__dict__['_test1'], 'MyEnum') + self.assertFalse(WhereEnum.__dict__.get('_test1', False)) + self.assertEqual(WhereEnum.__dict__['_test2'], 'OurEnum') + self.assertFalse(NeverEnum.__dict__.get('_test1', False)) + self.assertFalse(NeverEnum.__dict__.get('_test2', False)) + + +class TestStrEnum(TestCase): + + def test_set_name(self): + class Descriptor(object): + name = None + def __get__(self, instance, owner_class=None): + if instance is None: + return self + else: + return instance.__dict__[self.name] + def __set__(self, instance, value): + instance.__dict__[self.name] = value + def __set_name__(self, owner, name): + self.name = name + # + class AnEnum(Enum): + ONE = 'one' + two = Descriptor() + # + self.assertEqual(list(AnEnum), [AnEnum.ONE]) + self.assertEqual(AnEnum.two.name, 'two') + AnEnum.ONE.two = 'three' + self.assertEqual(AnEnum.ONE.two, 'three') + self.assertEqual(AnEnum.ONE.__dict__['two'], 'three') + + def test_private_names(self): + class Private(Enum): + __corporal = 'Radar' + __major_ = 'Hoolihan' + self.assertEqual(len(Private), 0) + self.assertEqual(Private._Private__corporal, 'Radar') + self.assertFalse(isinstance(Private._Private__corporal, Enum)) + self.assertEqual(Private._Private__major_, 'Hoolihan') + self.assertFalse(isinstance(Private._Private__major_, Enum)) + + def test_strenum_inherited_methods(self): + class phy(StrEnum): + pi = 'Pi' + tau = 'Tau' + self.assertTrue(phy.pi < phy.tau) + self.assertEqual(phy.pi.upper(), 'PI') + self.assertEqual(phy.tau.count('a'), 1) + + def test_strict_strenum(self): + for uhoh in (object, object(), [], Enum, 9): + with self.assertRaisesRegex(TypeError, r'values must be str'): + class Huh(StrEnum): + huh = uhoh + class Either(StrEnum): + _order_ = 'this that Those lower upper' + this = auto() + that = 'That' + Those = auto() + lower = 'lower' + upper = 'UPPER' + self.assertEqual([m.value for m in Either], ['this', 'That', 'those', 'lower', 'UPPER']) + # + with self.assertRaisesRegex(ValueError, r' is not lower-case'): + class Huh(LowerStrEnum): + huh = 'What' + # + class Lower(LowerStrEnum): + _order_ = 'this that Those lower upper' + this = auto() + that = 'that' + Those = auto() + lower = 'lower' + upper = 'upper' + self.assertEqual([m.value for m in Lower], ['this', 'that', 'those', 'lower', 'upper']) + # + with self.assertRaisesRegex(ValueError, r' is not upper-case'): + class Huh(UpperStrEnum): + huh = 'What' + # + class Upper(UpperStrEnum): + _order_ = 'this that Those lower upper' + this = auto() + that = 'THAT' + Those = auto() + lower = 'LOWER' + upper = 'UPPER' + self.assertEqual([m.value for m in Upper], ['THIS', 'THAT', 'THOSE', 'LOWER', 'UPPER']) + + def test_init_subclass(self): + class MyEnum(StrEnum): + def __init_subclass__(cls, **kwds): + super(MyEnum, cls).__init_subclass__(**kwds) + self.assertFalse(cls.__dict__.get('_test', False)) + cls._test1 = 'MyEnum' + # + class TheirEnum(MyEnum): + def __init_subclass__(cls, **kwds): + super(TheirEnum, cls).__init_subclass__(**kwds) + cls._test2 = 'TheirEnum' + class WhoseEnum(TheirEnum): + def __init_subclass__(cls, **kwds): + pass + class NoEnum(WhoseEnum): + ONE = 'one' + self.assertEqual(TheirEnum.__dict__['_test1'], 'MyEnum') + self.assertEqual(WhoseEnum.__dict__['_test1'], 'MyEnum') + self.assertEqual(WhoseEnum.__dict__['_test2'], 'TheirEnum') + self.assertFalse(NoEnum.__dict__.get('_test1', False)) + self.assertFalse(NoEnum.__dict__.get('_test2', False)) + # + class OurEnum(MyEnum): + def __init_subclass__(cls, **kwds): + cls._test2 = 'OurEnum' + class WhereEnum(OurEnum): + def __init_subclass__(cls, **kwds): + pass + class NeverEnum(WhereEnum): + ONE = 'one' + self.assertEqual(OurEnum.__dict__['_test1'], 'MyEnum') + self.assertFalse(WhereEnum.__dict__.get('_test1', False)) + self.assertEqual(WhereEnum.__dict__['_test2'], 'OurEnum') + self.assertFalse(NeverEnum.__dict__.get('_test1', False)) + self.assertFalse(NeverEnum.__dict__.get('_test2', False)) + + +class TestFlag(TestCase): + """Tests of the Flags.""" + + def setUp(self): + class Perm(Flag): + _order_ = 'R W X' + R, W, X = 4, 2, 1 + self.Perm = Perm + # + class Color(Flag): + BLACK = 0 + RED = 1 + ROJO = 1 + GREEN = 2 + BLUE = 4 + PURPLE = RED|BLUE + WHITE = RED|GREEN|BLUE + BLANCO = RED|GREEN|BLUE + self.Color = Color + # + class Fun(Flag): + _order_ = 'ONE TWO FOUR EIGHT' + ONE = auto() + TWO = auto() + THREE = ONE | TWO + FOUR = auto() + FIVE = FOUR | ONE + SIX = FOUR | TWO + SEVEN = FOUR | TWO | ONE + EIGHT = auto() + self.Fun = Fun + # + class TermColor(str, Flag): + def __new__(cls, value, code): + str_value = '\x1b[%sm' % code + obj = str.__new__(cls, str_value) + obj._value_ = value + obj.code = code + return obj + # + @classmethod + def _create_pseudo_member_values_(cls, members, *values): + code = ';'.join(m.code for m in members) + return values + (code, ) + # + AllReset = '0' # ESC [ 0 m # reset all (colors and brightness) + Bright = '1' # ESC [ 1 m # bright + Dim = '2' # ESC [ 2 m # dim (looks same as normal brightness) + Underline = '4' + Normal = '22' # ESC [ 22 m # normal brightness + # + # # FOREGROUND - 30s BACKGROUND - 40s: + FG_Black = '30' # ESC [ 30 m # black + FG_Red = '31' # ESC [ 31 m # red + FG_Green = '32' # ESC [ 32 m # green + FG_Yellow = '33' # ESC [ 33 m # yellow + FG_Blue = '34' # ESC [ 34 m # blue + FG_Magenta = '35' # ESC [ 35 m # magenta + FG_Cyan = '36' # ESC [ 36 m # cyan + FG_White = '37' # ESC [ 37 m # white + FG_Reset = '39' # ESC [ 39 m # reset + # + BG_Black = '40' # ESC [ 30 m # black + BG_Red = '41' # ESC [ 31 m # red + BG_Green = '42' # ESC [ 32 m # green + BG_Yellow = '43' # ESC [ 33 m # yellow + BG_Blue = '44' # ESC [ 34 m # blue + BG_Magenta = '45' # ESC [ 35 m # magenta + BG_Cyan = '46' # ESC [ 36 m # cyan + BG_White = '47' # ESC [ 37 m # white + BG_Reset = '49' # ESC [ 39 m # reset + # + __str__ = str.__str__ + # + def __repr__(self): + if self._name_ is not None: + return '<%s.%s>' % (self.__class__.__name__, self._name_) + else: + return '<%s: %s>' % (self.__class__.__name__, '|'.join([m.name for m in Flag.__iter__(self)])) + # + def __enter__(self): + print(self.AllReset, end='', verbose=0) + return self + # + def __exit__(self, *args): + print(self.AllReset, end='', verbose=0) + self.TermColor = TermColor + # + class Open(Flag): + RO = 0 + WO = 1 + RW = 2 + AC = 3 + CE = 1<<19 + self.Open = Open + + def test_set_name(self): + class Descriptor(object): + name = None + def __get__(self, instance, owner_class=None): + if instance is None: + return self + else: + return instance.__dict__[self.name] + def __set__(self, instance, value): + instance.__dict__[self.name] = value + def __set_name__(self, owner, name): + self.name = name + # + class AnEnum(Enum): + ONE = 1 + two = Descriptor() + # + self.assertEqual(list(AnEnum), [AnEnum.ONE]) + self.assertEqual(AnEnum.two.name, 'two') + AnEnum.ONE.two = 'three' + self.assertEqual(AnEnum.ONE.two, 'three') + self.assertEqual(AnEnum.ONE.__dict__['two'], 'three') + + def test_new_with_keywords(self): + class Huh(IntFlag): + __order__ = 'PLAIN BOLD_ITALIC HIGHLIGHT' + def __new__(cls, docstring, open=None, close=None): + if cls.__members__: + value = 2 ** (len(cls.__members__)-1) + else: + value = 0 + member = int.__new__(cls, value) + if open and close is None: + close = open + member.open = open + member.close = close + member.__doc__ = docstring + member._value_ = value + return member + PLAIN = 'normal' + BOLD_ITALIC = '***really super important***', '***' + HIGHLIGHT = 'please ==take notice==', '==', '==' + p = Huh.PLAIN + self.assertTrue(type(p) is Huh, type(p)) + self.assertEqual( + (p.value, p.__doc__, p.open, p.close), + (0, 'normal', None, None), + ) + bi = Huh.BOLD_ITALIC + self.assertEqual( + (bi.value, bi.__doc__, bi.open, bi.close), + (1, '***really super important***', '***', '***'), + ) + h = Huh.HIGHLIGHT + self.assertEqual( + (h.value, h.__doc__, h.open, h.close), + (2, 'please ==take notice==', '==', '=='), + ) + + def test_private_names(self): + class Private(Enum): + __corporal = 'Radar' + __major_ = 'Hoolihan' + self.assertEqual(len(Private), 0) + self.assertEqual(Private._Private__corporal, 'Radar') + self.assertFalse(isinstance(Private._Private__corporal, Enum)) + self.assertEqual(Private._Private__major_, 'Hoolihan') + self.assertFalse(isinstance(Private._Private__major_, Enum)) + + def test_auto_alias(self): + Fun = self.Fun + self.assertEqual( + list(Fun), + [Fun.ONE, Fun.TWO, Fun.FOUR, Fun.EIGHT], + ) + self.assertEqual(Fun.THREE._value_, 3) + self.assertEqual(repr(Fun.SEVEN), '') + self.assertEqual(list(Fun.SEVEN), [Fun.ONE, Fun.TWO, Fun.FOUR]) + + def test_str_is_str_str(self): + red, white = self.TermColor.FG_Red, self.TermColor.BG_White + barber = red | white + self.assertEqual(barber, '\x1b[31;47m') + self.assertEqual(barber.value, red.value | white.value) + self.assertEqual(barber.code, ';'.join([red.code, white.code])) + self.assertEqual(repr(barber), '') + self.assertEqual(str(barber), '\x1b[31;47m') + + def test_membership(self): + Color = self.Color + Open = self.Open + self.assertRaises(TypeError, lambda: 'BLACK' in Color) + self.assertRaises(TypeError, lambda: 'RO' in Open) + self.assertTrue(Color.BLACK in Color) + self.assertTrue(Open.RO in Open) + self.assertFalse(Color.BLACK in Open) + self.assertFalse(Open.RO in Color) + self.assertRaises(TypeError, lambda: 0 in Color) + self.assertRaises(TypeError, lambda: 0 in Open) + + def test_member_contains(self): + Color = self.Color + self.assertRaises(TypeError, lambda: 'test' in Color.BLUE) + self.assertRaises(TypeError, lambda: 2 in Color.BLUE) + self.assertTrue(Color.BLUE in Color.BLUE) + self.assertTrue(Color.BLUE in Color['RED|GREEN|BLUE']) + + def test_member_length(self): + self.assertEqual(self.Color.__len__(self.Color.BLACK), 0) + self.assertEqual(self.Color.__len__(self.Color.GREEN), 1) + self.assertEqual(self.Color.__len__(self.Color.PURPLE), 2) + self.assertEqual(self.Color.__len__(self.Color.BLANCO), 3) + + def test_number_reset_and_order_cleanup(self): + class Confused(Flag): + _order_ = 'ONE TWO FOUR DOS EIGHT SIXTEEN' + ONE = auto() + TWO = auto() + FOUR = auto() + DOS = 2 + EIGHT = auto() + SIXTEEN = auto() + self.assertEqual( + list(Confused), + [Confused.ONE, Confused.TWO, Confused.FOUR, Confused.EIGHT, Confused.SIXTEEN]) + self.assertIs(Confused.TWO, Confused.DOS) + self.assertEqual(Confused.DOS._value_, 2) + self.assertEqual(Confused.EIGHT._value_, 8) + self.assertEqual(Confused.SIXTEEN._value_, 16) + + def test_str(self): + Perm = self.Perm + self.assertEqual(str(Perm.R), 'Perm.R') + self.assertEqual(str(Perm.W), 'Perm.W') + self.assertEqual(str(Perm.X), 'Perm.X') + self.assertEqual(str(Perm.R | Perm.W), 'Perm.R|W') + self.assertEqual(str(Perm.R | Perm.W | Perm.X), 'Perm.R|W|X') + self.assertEqual(str(Perm(0)), 'Perm(0)') + self.assertEqual(str(~Perm.R), 'Perm.W|X') + self.assertEqual(str(~Perm.W), 'Perm.R|X') + self.assertEqual(str(~Perm.X), 'Perm.R|W') + self.assertEqual(str(~(Perm.R | Perm.W)), 'Perm.X') + self.assertEqual(str(~(Perm.R | Perm.W | Perm.X)), 'Perm(0)') + self.assertEqual(str(Perm(-1)), 'Perm.R|W|X') + self.assertEqual(str(Perm(~0)), 'Perm.R|W|X') + + Open = self.Open + self.assertEqual(str(Open.RO), 'Open.RO') + self.assertEqual(str(Open.WO), 'Open.WO') + self.assertEqual(str(Open.AC), 'Open.AC') + self.assertEqual(str(Open.RO | Open.CE), 'Open.CE') + self.assertEqual(str(Open.WO | Open.CE), 'Open.WO|CE') + self.assertEqual(str(~Open.RO), 'Open.WO|RW|CE') + self.assertEqual(str(~Open.WO), 'Open.RW|CE') + self.assertEqual(str(~Open.AC), 'Open.CE') + self.assertEqual(str(~(Open.RO | Open.CE)), 'Open.AC') + self.assertEqual(str(~(Open.WO | Open.CE)), 'Open.RW') + + def test_repr(self): + Perm = self.Perm + self.assertEqual(repr(Perm.R), '') + self.assertEqual(repr(Perm.W), '') + self.assertEqual(repr(Perm.X), '') + self.assertEqual(repr(Perm.R | Perm.W), '') + self.assertEqual(repr(Perm.R | Perm.W | Perm.X), '') + self.assertEqual(repr(Perm(0)), '') + self.assertEqual(repr(~Perm.R), '') + self.assertEqual(repr(~Perm.W), '') + self.assertEqual(repr(~Perm.X), '') + self.assertEqual(repr(~(Perm.R | Perm.W)), '') + self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '') + self.assertEqual(repr(Perm(~0)), '') + + Open = self.Open + self.assertEqual(repr(Open.RO), '') + self.assertEqual(repr(Open.WO), '') + self.assertEqual(repr(Open.AC), '') + self.assertEqual(repr(Open.RO | Open.CE), '') + self.assertEqual(repr(Open.WO | Open.CE), '') + self.assertEqual(repr(~Open.RO), '') + self.assertEqual(repr(~Open.WO), '') + self.assertEqual(repr(~Open.AC), '') + self.assertEqual(repr(~(Open.RO | Open.CE)), '') + self.assertEqual(repr(~(Open.WO | Open.CE)), '') + + def test_name_lookup(self): + Color = self.Color + self.assertTrue(Color.RED is Color['RED']) + self.assertTrue(Color.RED|Color.GREEN is Color['RED|GREEN']) + self.assertTrue(Color.PURPLE is Color['RED|BLUE']) + + def test_or(self): + Perm = self.Perm + for i in Perm: + for j in Perm: + self.assertEqual((i | j), Perm(i.value | j.value)) + self.assertEqual((i | j).value, i.value | j.value) + self.assertIs(type(i | j), Perm) + for i in Perm: + self.assertIs(i | i, i) + Open = self.Open + self.assertIs(Open.RO | Open.CE, Open.CE) + + def test_and(self): + Perm = self.Perm + RW = Perm.R | Perm.W + RX = Perm.R | Perm.X + WX = Perm.W | Perm.X + RWX = Perm.R | Perm.W | Perm.X + values = list(Perm) + [RW, RX, WX, RWX, Perm(0)] + for i in values: + for j in values: + self.assertEqual((i & j).value, i.value & j.value) + self.assertIs(type(i & j), Perm) + for i in Perm: + self.assertIs(i & i, i) + self.assertIs(i & RWX, i) + self.assertIs(RWX & i, i) + Open = self.Open + self.assertIs(Open.RO & Open.CE, Open.RO) + + def test_xor(self): + Perm = self.Perm + for i in Perm: + for j in Perm: + self.assertEqual((i ^ j).value, i.value ^ j.value) + self.assertIs(type(i ^ j), Perm) + for i in Perm: + self.assertIs(i ^ Perm(0), i) + self.assertIs(Perm(0) ^ i, i) + Open = self.Open + self.assertIs(Open.RO ^ Open.CE, Open.CE) + self.assertIs(Open.CE ^ Open.CE, Open.RO) + + def test_invert(self): + Perm = self.Perm + RW = Perm.R | Perm.W + RX = Perm.R | Perm.X + WX = Perm.W | Perm.X + RWX = Perm.R | Perm.W | Perm.X + values = list(Perm) + [RW, RX, WX, RWX, Perm(0)] + for i in values: + self.assertIs(type(~i), Perm) + self.assertEqual(~~i, i) + for i in Perm: + self.assertIs(~~i, i) + Open = self.Open + self.assertIs(Open.WO & ~Open.WO, Open.RO) + self.assertIs((Open.WO|Open.CE) & ~Open.WO, Open.CE) + + def test_bool(self): + Perm = self.Perm + for f in Perm: + self.assertTrue(f) + Open = self.Open + for f in Open: + self.assertEqual(bool(f.value), bool(f)) + + def test_doc_flag(self): + class DocFlag(Flag): + _init_ = 'value __doc__' + _start_ = 0 + # def __new__(cls, value, doc=None): + # # if doc is None and isinstance(value, basestring): + # # value, doc = doc, value + # # if value is None: + # # if not len(cls): + # # value = 0 + # # else: + # # value = 2 ** (len(cls) -1) + # # if not isinstance(value, baseinteger): + # # raise TypeError("%r is not a valid %s value" % (value, cls.__name__)) + # obj = object.__new__(cls) + # # if doc is None, don't mess with the value + # if doc: + # value = value >> 1 + # obj._value_ = value + # obj.__doc__ = doc + # return obj + # + class AddressSegment(DocFlag): + _order_ = 'UNKNOWN PO PO_TYPE NUMBER PREORD NAME STREET POSTORD SECONDARY_TYPE SECONDARY_NUMBER AND' + UNKNOWN = "unable to determine address element type" + PO = "post office delivery" + PO_TYPE = "box or drawer" + NUMBER = "main unit designator" + PREORD = "N S E W etc" + NAME = "street name" + STREET = "st ave blvd etc" + POSTORD = "N S E W etc" + SECONDARY_TYPE = "apt bldg floor etc" + SECONDARY_NUMBER = "secondary unit designator" + AND = "& indicates a corner address" + AS = AddressSegment + self.assertEqual(AS.NAME._value_, 16) + self.assertEqual(AS.STREET._value_, 32) + self.assertEqual(AS.SECONDARY_TYPE._value_, 128) + self.assertEqual((AS.NAME | AS.STREET)._value_, 48, "%r is not 48" % (AS.NAME | AS.STREET)) + + def test_iteration(self): + C = self.Color + self.assertEqual(list(C), [C.RED, C.GREEN, C.BLUE]) + self.assertEqual(list(C.PURPLE), [C.RED, C.BLUE]) + + def test_member_iteration(self): + C = self.Color + self.assertEqual(list(C.BLACK), []) + self.assertEqual(list(C.RED), [C.RED]) + self.assertEqual(list(C.PURPLE), [C.RED, C.BLUE]) + + def test_programatic_function_string(self): + Perm = Flag('Perm', 'R W X') + lst = list(Perm) + self.assertEqual(len(lst), len(Perm)) + self.assertEqual(len(Perm), 3, Perm) + self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) + for i, n in enumerate('R W X'.split()): + v = 1<' % (self.__class__.__name__, self._name_) + self.assertTrue(isinstance(Color.FG_Black, Color)) + self.assertTrue(isinstance(Color.FG_Black, str)) + self.assertEqual(Color.FG_Black, '\x1b[30m') + self.assertEqual(Color.FG_Black.code, '30') + colors = Color.BG_Magenta | Color.FG_Black + self.assertTrue(isinstance(colors, Color)) + self.assertTrue(isinstance(colors, str)) + self.assertEqual(colors, '\x1b[30;45m') + self.assertEqual(colors.code, '30;45') + self.assertEqual(repr(colors), '') + + def test_sub_subclass_with_new_new(self): + class StrFlag(str, Flag): + def __new__(cls, value, code): + str_value = '\x1b[%sm' % code + obj = str.__new__(cls, str_value) + obj._value_ = value + obj.code = code + return obj + @classmethod + def _create_pseudo_member_(cls, value): + # calculate the code + members = list(cls._iter_member_(value)) + code = ';'.join(m.code for m in members) + pseudo_member = super(StrFlag, cls)._create_pseudo_member_(value, code) + return pseudo_member + # + class Color(StrFlag): + _order_ = 'FG_Black FG_Red FG_Green FG_Blue BG_Yellow BG_Magenta BG_Cyan BG_White' + def __new__(cls, value, string, abbr): + str_value = (abbr or '').title() + obj = str.__new__(cls, str_value) + obj._value_ = value + obj.code = string + obj.abbr = abbr + return obj + # # FOREGROUND - 30s BACKGROUND - 40s: + FG_Black = '30', 'blk' # ESC [ 30 m # black + FG_Red = '31', 'red' # ESC [ 31 m # red + FG_Green = '32', 'grn' # ESC [ 32 m # green + FG_Blue = '34', 'blu' # ESC [ 34 m # blue + # + BG_Yellow = '43', 'ylw' # ESC [ 33 m # yellow + BG_Magenta = '45', 'mag' # ESC [ 35 m # magenta + BG_Cyan = '46', 'cyn' # ESC [ 36 m # cyan + BG_White = '47', 'wht' # ESC [ 37 m # white + # + def __repr__(self): + if self._name_ is not None: + return '<%s.%s>' % (self.__class__.__name__, self._name_) + else: + return '<%s: %s>' % (self.__class__.__name__, '|'.join([m.name for m in self])) + self.assertTrue(isinstance(Color.FG_Black, Color)) + self.assertTrue(isinstance(Color.FG_Black, str)) + self.assertEqual(Color.FG_Black, 'Blk', str.__repr__(Color.FG_Black)) + self.assertEqual(Color.FG_Black.abbr, 'blk') + + def test_subclass_with_default_new(self): + class MyFlag(str, Flag): + _order_ = 'this these theother' + this = 'that' + these = 'those' + theother = 'thingimibobs' + self.assertEqual(MyFlag.this, 'that') + self.assertEqual(MyFlag.this.value, 1) + self.assertEqual(MyFlag.these, 'those') + self.assertEqual(MyFlag.these.value, 2) + self.assertEqual(MyFlag.theother, 'thingimibobs') + self.assertEqual(MyFlag.theother.value, 4) + + def test_subclass_a_bunch(self): + class Color(str, Flag): + _order_ = 'FG_Black FG_Red FG_Green FG_Blue BG_Yellow BG_Magenta BG_Cyan BG_White' + def __new__(cls, value, code): + str_value = '\x1b[%sm' % code + obj = str.__new__(cls, str_value) + obj._value_ = value + obj.code = code + return obj + @staticmethod + def _generate_next_value_(name, start, count, values, *args, **kwds): + return (2 ** count, ) + args + @classmethod + def _create_pseudo_member_(cls, value): + # calculate the code + members = list(cls._iter_member_(value)) + code = ';'.join(m.code for m in members) + pseudo_member = super(Color, cls)._create_pseudo_member_(value, code) + return pseudo_member + # + # # FOREGROUND - 30s BACKGROUND - 40s: + FG_Black = '30' # ESC [ 30 m # black + FG_Red = '31' # ESC [ 31 m # red + FG_Green = '32' # ESC [ 32 m # green + FG_Blue = '34' # ESC [ 34 m # blue + # + BG_Yellow = '43' # ESC [ 33 m # yellow + BG_Magenta = '45' # ESC [ 35 m # magenta + BG_Cyan = '46' # ESC [ 36 m # cyan + BG_White = '47' # ESC [ 37 m # white + # + def __repr__(self): + if self._name_ is not None: + return '<%s.%s>' % (self.__class__.__name__, self._name_) + else: + return '<%s: %s>' % (self.__class__.__name__, '|'.join([m.name for m in self])) + # + Purple = Color.BG_Magenta | Color.FG_Blue + self.assertTrue(isinstance(Purple, Color)) + self.assertTrue(isinstance(Purple, str)) + self.assertIs(Purple, Color.BG_Magenta | Color.FG_Blue) + self.assertEqual(Purple, '\x1b[34;45m') + self.assertEqual(Purple.code, '34;45') + self.assertEqual(Purple.name, 'FG_Blue|BG_Magenta') + + def test_init_subclass(self): + class MyEnum(Flag): + def __init_subclass__(cls, **kwds): + super(MyEnum, cls).__init_subclass__(**kwds) + self.assertFalse(cls.__dict__.get('_test', False)) + cls._test1 = 'MyEnum' + # + class TheirEnum(MyEnum): + def __init_subclass__(cls, **kwds): + super(TheirEnum, cls).__init_subclass__(**kwds) + cls._test2 = 'TheirEnum' + class WhoseEnum(TheirEnum): + def __init_subclass__(cls, **kwds): + pass + class NoEnum(WhoseEnum): + ONE = 1 + self.assertEqual(TheirEnum.__dict__['_test1'], 'MyEnum') + self.assertEqual(WhoseEnum.__dict__['_test1'], 'MyEnum') + self.assertEqual(WhoseEnum.__dict__['_test2'], 'TheirEnum') + self.assertFalse(NoEnum.__dict__.get('_test1', False)) + self.assertFalse(NoEnum.__dict__.get('_test2', False)) + # + class OurEnum(MyEnum): + def __init_subclass__(cls, **kwds): + cls._test2 = 'OurEnum' + class WhereEnum(OurEnum): + def __init_subclass__(cls, **kwds): + pass + class NeverEnum(WhereEnum): + ONE = 1 + self.assertEqual(OurEnum.__dict__['_test1'], 'MyEnum') + self.assertFalse(WhereEnum.__dict__.get('_test1', False)) + self.assertEqual(WhereEnum.__dict__['_test2'], 'OurEnum') + self.assertFalse(NeverEnum.__dict__.get('_test1', False)) + self.assertFalse(NeverEnum.__dict__.get('_test2', False)) + + def test_int_long_conversion(self): + class Perm(Flag): + EXEC = 1 << 0 + WRITE = 1 << 1 + READ = 1 << 2 + MSB32 = 1 << 31 + MSB64 = 1 << 63 + + # 32-bit system test + self.assertEqual(Perm.MSB32, Perm(0x80000000)) + self.assertEqual(Perm.WRITE|Perm.MSB32, Perm(0x80000002)) + + # 64-bit system test + self.assertEqual(Perm.MSB64, Perm(0x8000000000000000)) + self.assertEqual(Perm.MSB64|Perm.WRITE, Perm(0x8000000000000002)) + + +class TestIntFlag(TestCase): + """Tests of the IntFlags.""" + + def setUp(self): + # + class Perm(IntFlag): + _order_ = 'R W X' + R = 1 << 2 + W = 1 << 1 + X = 1 << 0 + # + class Color(IntFlag): + BLACK = 0 + RED = 1 + GREEN = 2 + BLUE = 4 + PURPLE = RED|BLUE + # + class Open(IntFlag): + "not a good flag candidate" + RO = 0 + WO = 1 + RW = 2 + AC = 3 + CE = 1<<19 + # + self.Perm = Perm + self.Color = Color + self.Open = Open + + def test_set_name(self): + class Descriptor(object): + name = None + def __get__(self, instance, owner_class=None): + if instance is None: + return self + else: + return instance.__dict__[self.name] + def __set__(self, instance, value): + instance.__dict__[self.name] = value + def __set_name__(self, owner, name): + self.name = name + # + class AnEnum(Enum): + ONE = 1 + two = Descriptor() + # + self.assertEqual(list(AnEnum), [AnEnum.ONE]) + self.assertEqual(AnEnum.two.name, 'two') + AnEnum.ONE.two = 'three' + self.assertEqual(AnEnum.ONE.two, 'three') + self.assertEqual(AnEnum.ONE.__dict__['two'], 'three') + + def test_private_names(self): + class Private(Enum): + __corporal = 'Radar' + __major_ = 'Hoolihan' + self.assertEqual(len(Private), 0) + self.assertEqual(Private._Private__corporal, 'Radar') + self.assertFalse(isinstance(Private._Private__corporal, Enum)) + self.assertEqual(Private._Private__major_, 'Hoolihan') + self.assertFalse(isinstance(Private._Private__major_, Enum)) + + def test_membership(self): + Color = self.Color + Open = self.Open + self.assertRaises(TypeError, lambda: 'GREEN' in Color) + self.assertRaises(TypeError, lambda: 'RW' in Open) + self.assertTrue(Color.GREEN in Color) + self.assertTrue(Open.RW in Open) + self.assertFalse(Color.GREEN in Open) + self.assertFalse(Open.RW in Color) + self.assertRaises(TypeError, lambda: 2 in Color) + self.assertRaises(TypeError, lambda: 2 in Open) + + def test_member_contains(self): + Color = self.Color + self.assertRaises(TypeError, lambda: 'test' in Color.RED) + self.assertRaises(TypeError, lambda: 1 in Color.RED) + self.assertTrue(Color.RED in Color.RED) + self.assertTrue(Color.RED in Color.PURPLE) + + def test_name_lookup(self): + Color = self.Color + self.assertTrue(Color.RED is Color['RED']) + self.assertTrue(Color.RED|Color.GREEN is Color['RED|GREEN']) + self.assertTrue(Color.PURPLE is Color['RED|BLUE']) + + def test_type(self): + Perm = self.Perm + Open = self.Open + for f in Perm: + self.assertTrue(isinstance(f, Perm)) + self.assertEqual(f, f.value) + self.assertTrue(isinstance(Perm.W | Perm.X, Perm)) + self.assertEqual(Perm.W | Perm.X, 3) + for f in Open: + self.assertTrue(isinstance(f, Open)) + self.assertEqual(f, f.value) + self.assertTrue(isinstance(Open.WO | Open.RW, Open)) + self.assertEqual(Open.WO | Open.RW, 3) + + + def test_str(self): + Perm = self.Perm + self.assertEqual(str(Perm.R), '4') + self.assertEqual(str(Perm.W), '2') + self.assertEqual(str(Perm.X), '1') + self.assertEqual(str(Perm.R | Perm.W), '6') + self.assertEqual(str(Perm.R | Perm.W | Perm.X), '7') + self.assertEqual(str(Perm(0)), '0') + self.assertEqual(str(~Perm.R), '3') + self.assertEqual(str(~Perm.W), '5') + self.assertEqual(str(~Perm.X), '6') + self.assertEqual(str(~(Perm.R | Perm.W)), '1') + self.assertEqual(str(~(Perm.R | Perm.W | Perm.X)), '0') + self.assertEqual(str(Perm(~0)), '7') + + Open = self.Open + self.assertEqual(str(Open.RO), '0') + self.assertEqual(str(Open.WO), '1') + self.assertEqual(str(Open.AC), '3') + self.assertEqual(str(Open.RO | Open.CE), '524288') + self.assertEqual(str(Open.WO | Open.CE), '524289') + self.assertEqual(str(~Open.RO), '524291') + self.assertEqual(str(~Open.WO), '524290') + self.assertEqual(str(~Open.AC), '524288') + self.assertEqual(str(~(Open.RO | Open.CE)), '3') + self.assertEqual(str(~(Open.WO | Open.CE)), '2') + + def test_repr_strict(self): + class Perm(IntFlag): + _order_ = 'R W X' + R = 1 << 2 + W = 1 << 1 + X = 1 << 0 + Perm._boundary_ = aenum.STRICT + self.assertEqual(repr(Perm.R), '') + self.assertEqual(repr(Perm.W), '') + self.assertEqual(repr(Perm.X), '') + self.assertEqual(repr(Perm.R | Perm.W), '') + self.assertEqual(repr(Perm.R | Perm.W | Perm.X), '') + self.assertEqual(repr(Perm(0)), '') + self.assertEqual(repr(~Perm.R), '') + self.assertEqual(repr(~Perm.W), '') + self.assertEqual(repr(~Perm.X), '') + self.assertEqual(repr(~(Perm.R | Perm.W)), '') + self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '') + # + with self.assertRaisesRegex(ValueError, r'invalid value: 12'): + repr(Perm.R | 8) + with self.assertRaisesRegex(ValueError, r'invalid value: 12'): + repr(~(Perm.R | 8)) + with self.assertRaisesRegex(ValueError, r'invalid value: -9'): + repr(Perm(~8)) + + def test_repr_conform(self): + class Perm(IntFlag): + _order_ = 'R W X' + R = 1 << 2 + W = 1 << 1 + X = 1 << 0 + Perm._boundary_ = aenum.CONFORM + self.assertEqual(repr(Perm.R), '') + self.assertEqual(repr(Perm.W), '') + self.assertEqual(repr(Perm.X), '') + self.assertEqual(repr(Perm.R | Perm.W), '') + self.assertEqual(repr(Perm.R | Perm.W | Perm.X), '') + self.assertEqual(repr(Perm(0)), '') + self.assertEqual(repr(~Perm.R), '') + self.assertEqual(repr(~Perm.W), '') + self.assertEqual(repr(~Perm.X), '') + self.assertEqual(repr(~(Perm.R | Perm.W)), '') + self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '') + self.assertEqual(repr(Perm.R | 8), '') + self.assertEqual(repr(Perm(8)), '') + self.assertEqual(repr(~(Perm.R | 8)), '') + self.assertEqual(repr(Perm(~8)), '') + + def test_repr_eject(self): + class Perm(IntFlag): + _order_ = 'R W X' + _boundary_ = EJECT + R = 1 << 2 + W = 1 << 1 + X = 1 << 0 + self.assertEqual(repr(Perm.R), '') + self.assertEqual(repr(Perm.W), '') + self.assertEqual(repr(Perm.X), '') + self.assertEqual(repr(Perm.R | Perm.W), '') + self.assertEqual(repr(Perm.R | Perm.W | Perm.X), '') + self.assertEqual(repr(Perm(0)), '') + self.assertEqual(repr(~Perm.R), '') + self.assertEqual(repr(~Perm.W), '') + self.assertEqual(repr(~Perm.X), '') + self.assertEqual(repr(~(Perm.R | Perm.W)), '') + self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '') + self.assertEqual(repr(Perm.R | 8), '12') + self.assertEqual(repr(Perm(8)), '8') + self.assertEqual(repr(~(Perm.R | 8)), '-13') + self.assertEqual(repr(Perm(~8)), '-9') + + def test_repr_open(self): + class Open(IntFlag): + "not a good flag candidate" + RO = 0 + WO = 1 + RW = 2 + AC = 3 + CE = 1<<19 + Open._boundary_ = aenum.STRICT + self.assertEqual(repr(Open.RO), '') + self.assertEqual(repr(Open.WO), '') + self.assertEqual(repr(Open.AC), '') + self.assertEqual(repr(Open.RO | Open.CE), '') + self.assertEqual(repr(Open.WO | Open.CE), '') + self.assertEqual(repr(~Open.RO), '') + self.assertEqual(repr(~Open.WO), '') + self.assertEqual(repr(~Open.AC), '') + self.assertEqual(repr(~(Open.RO | Open.CE)), '') + self.assertEqual(repr(~(Open.WO | Open.CE)), '') + with self.assertRaisesRegex(ValueError, r'invalid value: -5'): + repr(Open(~4)) + with self.assertRaisesRegex(ValueError, r'invalid value: 4'): + repr(Open(4)) + # + class Open(IntFlag): + "not a good flag candidate" + RO = 0 + WO = 1 + RW = 2 + AC = 3 + CE = 1<<19 + Open._boundary_ = aenum.CONFORM + self.assertEqual(repr(Open.RO), '') + self.assertEqual(repr(Open.WO), '') + self.assertEqual(repr(Open.AC), '') + self.assertEqual(repr(Open.RO | Open.CE), '') + self.assertEqual(repr(Open.WO | Open.CE), '') + self.assertEqual(repr(~Open.RO), '') + self.assertEqual(repr(~Open.WO), '') + self.assertEqual(repr(~Open.AC), '') + self.assertEqual(repr(~(Open.RO | Open.CE)), '') + self.assertEqual(repr(~(Open.WO | Open.CE)), '') + self.assertEqual(repr(Open(~4)), '') + self.assertEqual(repr(Open(4)), '') + # + class Open(IntFlag): + "not a good flag candidate" + RO = 0 + WO = 1 + RW = 2 + AC = 3 + CE = 1<<19 + Open._boundary_ = aenum.EJECT + self.assertEqual(repr(Open.RO), '') + self.assertEqual(repr(Open.WO), '') + self.assertEqual(repr(Open.AC), '') + self.assertEqual(repr(Open.RO | Open.CE), '') + self.assertEqual(repr(Open.WO | Open.CE), '') + self.assertEqual(repr(~Open.RO), '') + self.assertEqual(repr(~Open.WO), '') + self.assertEqual(repr(~Open.AC), '') + self.assertEqual(repr(~(Open.RO | Open.CE)), '') + self.assertEqual(repr(~(Open.WO | Open.CE)), '') + self.assertEqual(repr(Open(~4)), '-5') + self.assertEqual(repr(Open(4)), '4') + + def test_or(self): + Perm = self.Perm + for i in Perm: + for j in Perm: + self.assertEqual(i | j, i.value | j.value) + self.assertEqual((i | j).value, i.value | j.value) + self.assertIs(type(i | j), Perm) + for j in range(8): + self.assertEqual(i | j, i.value | j) + self.assertEqual((i | j).value, i.value | j) + self.assertIs(type(i | j), Perm) + self.assertEqual(j | i, j | i.value) + self.assertEqual((j | i).value, j | i.value) + self.assertIs(type(j | i), Perm) + for i in Perm: + self.assertIs(i | i, i) + self.assertIs(i | 0, i) + self.assertIs(0 | i, i) + Open = self.Open + self.assertIs(Open.RO | Open.CE, Open.CE) + + def test_and(self): + Perm = self.Perm + RW = Perm.R | Perm.W + RX = Perm.R | Perm.X + WX = Perm.W | Perm.X + RWX = Perm.R | Perm.W | Perm.X + values = list(Perm) + [RW, RX, WX, RWX, Perm(0)] + for i in values: + for j in values: + self.assertEqual(i & j, i.value & j.value, 'i is %r, j is %r' % (i, j)) + self.assertEqual((i & j).value, i.value & j.value, 'i is %r, j is %r' % (i, j)) + self.assertIs(type(i & j), Perm, 'i is %r, j is %r' % (i, j)) + for j in range(8): + self.assertEqual(i & j, i.value & j) + self.assertEqual((i & j).value, i.value & j) + self.assertIs(type(i & j), Perm) + self.assertEqual(j & i, j & i.value) + self.assertEqual((j & i).value, j & i.value) + self.assertIs(type(j & i), Perm) + for i in Perm: + self.assertIs(i & i, i) + self.assertIs(i & 7, i) + self.assertIs(7 & i, i) + Open = self.Open + self.assertIs(Open.RO & Open.CE, Open.RO) + + def test_xor(self): + Perm = self.Perm + for i in Perm: + for j in Perm: + self.assertEqual(i ^ j, i.value ^ j.value) + self.assertEqual((i ^ j).value, i.value ^ j.value) + self.assertIs(type(i ^ j), Perm) + for j in range(8): + self.assertEqual(i ^ j, i.value ^ j) + self.assertEqual((i ^ j).value, i.value ^ j) + self.assertIs(type(i ^ j), Perm) + self.assertEqual(j ^ i, j ^ i.value) + self.assertEqual((j ^ i).value, j ^ i.value) + self.assertIs(type(j ^ i), Perm) + for i in Perm: + self.assertIs(i ^ 0, i) + self.assertIs(0 ^ i, i) + Open = self.Open + self.assertIs(Open.RO ^ Open.CE, Open.CE) + self.assertIs(Open.CE ^ Open.CE, Open.RO) + + def test_invert(self): + Perm = self.Perm + RW = Perm.R | Perm.W + RX = Perm.R | Perm.X + WX = Perm.W | Perm.X + RWX = Perm.R | Perm.W | Perm.X + values = list(Perm) + [RW, RX, WX, RWX, Perm(0)] + for i in values: + self.assertEqual(~i, (~i).value) + self.assertIs(type(~i), Perm) + self.assertEqual(~~i, i) + for i in Perm: + self.assertIs(~~i, i) + Open = self.Open + self.assertIs(Open.WO & ~Open.WO, Open.RO) + self.assertIs((Open.WO|Open.CE) & ~Open.WO, Open.CE) + + def test_iter(self): + Perm = self.Perm + NoPerm = Perm.R ^ Perm.R + RWX = Perm.R | Perm.W | Perm.X + self.assertEqual(list(NoPerm), []) + self.assertEqual(list(Perm.R), [Perm.R]) + self.assertEqual(list(RWX), [Perm.R, Perm.W, Perm.X]) + + def test_programatic_function_string(self): + Perm = IntFlag('Perm', 'R W X') + lst = list(Perm) + self.assertEqual(len(lst), len(Perm)) + self.assertEqual(len(Perm), 3, Perm) + self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) + for i, n in enumerate('R W X'.split()): + v = 1< one' in message) + + try: + class Dirtier(IntEnum): + __order__ = 'single triple' + single = 1 + double = 1 + triple = 3 + turkey = 3 + unique(Dirtier) + except ValueError: + exc = sys.exc_info()[1] + message = exc.args[0] + self.assertTrue('double -> single' in message) + self.assertTrue('turkey -> triple' in message) + + def test_unique_with_name(self): + @unique + class Silly(Enum): + one = 1 + two = 'dos' + name = 3 + @unique + class Sillier(IntEnum): + single = 1 + name = 2 + triple = 3 + value = 4 + + +class TestNamedTuple(TestCase): + + def test_explicit_indexing(self): + class Person(NamedTuple): + age = 0 + first = 1 + last = 2 + p1 = Person(17, 'John', 'Doe') + p2 = Person(21, 'Jane', 'Doe') + self.assertEqual(p1[0], 17) + self.assertEqual(p1[1], 'John') + self.assertEqual(p1[2], 'Doe') + self.assertEqual(p2[0], 21) + self.assertEqual(p2[1], 'Jane') + self.assertEqual(p2[2], 'Doe') + self.assertEqual(p1.age, 17) + self.assertEqual(p1.first, 'John') + self.assertEqual(p1.last, 'Doe') + self.assertEqual(p2.age, 21) + self.assertEqual(p2.first, 'Jane') + self.assertEqual(p2.last, 'Doe') + + def test_implicit_indexing(self): + class Person(NamedTuple): + __order__ = "age first last" + age = "person's age" + first = "person's first name" + last = "person's last name" + p1 = Person(17, 'John', 'Doe') + p2 = Person(21, 'Jane', 'Doe') + self.assertEqual(p1[0], 17) + self.assertEqual(p1[1], 'John') + self.assertEqual(p1[2], 'Doe') + self.assertEqual(p2[0], 21) + self.assertEqual(p2[1], 'Jane') + self.assertEqual(p2[2], 'Doe') + self.assertEqual(p1.age, 17) + self.assertEqual(p1.first, 'John') + self.assertEqual(p1.last, 'Doe') + self.assertEqual(p2.age, 21) + self.assertEqual(p2.first, 'Jane') + self.assertEqual(p2.last, 'Doe') + + def test_mixed_indexing(self): + class Person(NamedTuple): + __order__ = "age last cars" + age = "person's age" + last = 2, "person's last name" + cars = "person's cars" + p1 = Person(17, 'John', 'Doe', 3) + p2 = Person(21, 'Jane', 'Doe', 9) + self.assertEqual(p1[0], 17) + self.assertEqual(p1[1], 'John') + self.assertEqual(p1[2], 'Doe') + self.assertEqual(p1[3], 3) + self.assertEqual(p2[0], 21) + self.assertEqual(p2[1], 'Jane') + self.assertEqual(p2[2], 'Doe') + self.assertEqual(p2[3], 9) + self.assertEqual(p1.age, 17) + self.assertEqual(p1.last, 'Doe') + self.assertEqual(p1.cars, 3) + self.assertEqual(p2.age, 21) + self.assertEqual(p2.last, 'Doe') + self.assertEqual(p2.cars, 9) + + def test_issubclass(self): + class Person(NamedTuple): + age = 0 + first = 1 + last = 2 + self.assertTrue(issubclass(Person, NamedTuple)) + self.assertTrue(issubclass(Person, tuple)) + + def test_isinstance(self): + class Person(NamedTuple): + age = 0 + first = 1 + last = 2 + p1 = Person(17, 'John', 'Doe') + self.assertTrue(isinstance(p1, Person)) + self.assertTrue(isinstance(p1, NamedTuple)) + self.assertTrue(isinstance(p1, tuple)) + + def test_explicit_indexing_after_functional_api(self): + Person = NamedTuple('Person', (('age', 0), ('first', 1), ('last', 2))) + p1 = Person(17, 'John', 'Doe') + p2 = Person(21, 'Jane', 'Doe') + self.assertEqual(p1[0], 17) + self.assertEqual(p1[1], 'John') + self.assertEqual(p1[2], 'Doe') + self.assertEqual(p2[0], 21) + self.assertEqual(p2[1], 'Jane') + self.assertEqual(p2[2], 'Doe') + self.assertEqual(p1.age, 17) + self.assertEqual(p1.first, 'John') + self.assertEqual(p1.last, 'Doe') + self.assertEqual(p2.age, 21) + self.assertEqual(p2.first, 'Jane') + self.assertEqual(p2.last, 'Doe') + + def test_implicit_indexing_after_functional_api(self): + Person = NamedTuple('Person', 'age first last') + p1 = Person(17, 'John', 'Doe') + p2 = Person(21, 'Jane', 'Doe') + self.assertEqual(p1[0], 17) + self.assertEqual(p1[1], 'John') + self.assertEqual(p1[2], 'Doe') + self.assertEqual(p2[0], 21) + self.assertEqual(p2[1], 'Jane') + self.assertEqual(p2[2], 'Doe') + self.assertEqual(p1.age, 17) + self.assertEqual(p1.first, 'John') + self.assertEqual(p1.last, 'Doe') + self.assertEqual(p2.age, 21) + self.assertEqual(p2.first, 'Jane') + self.assertEqual(p2.last, 'Doe') + + def test_mixed_indexing_after_functional_api(self): + Person = NamedTuple('Person', (('age', 0), ('last', 2), ('cars', 3))) + p1 = Person(17, 'John', 'Doe', 3) + p2 = Person(21, 'Jane', 'Doe', 9) + self.assertEqual(p1[0], 17) + self.assertEqual(p1[1], 'John') + self.assertEqual(p1[2], 'Doe') + self.assertEqual(p1[3], 3) + self.assertEqual(p2[0], 21) + self.assertEqual(p2[1], 'Jane') + self.assertEqual(p2[2], 'Doe') + self.assertEqual(p2[3], 9) + self.assertEqual(p1.age, 17) + self.assertEqual(p1.last, 'Doe') + self.assertEqual(p1.cars, 3) + self.assertEqual(p2.age, 21) + self.assertEqual(p2.last, 'Doe') + self.assertEqual(p2.cars, 9) + + def test_issubclass_after_functional_api(self): + Person = NamedTuple('Person', 'age first last') + self.assertTrue(issubclass(Person, NamedTuple)) + self.assertTrue(issubclass(Person, tuple)) + + def test_isinstance_after_functional_api(self): + Person = NamedTuple('Person', 'age first last') + p1 = Person(17, 'John', 'Doe') + self.assertTrue(isinstance(p1, Person)) + self.assertTrue(isinstance(p1, NamedTuple)) + self.assertTrue(isinstance(p1, tuple)) + + def test_creation_with_all_keywords(self): + Person = NamedTuple('Person', 'age first last') + p1 = Person(age=17, first='John', last='Doe') + self.assertEqual(p1[0], 17) + self.assertEqual(p1[1], 'John') + self.assertEqual(p1[2], 'Doe') + self.assertEqual(p1.age, 17) + self.assertEqual(p1.first, 'John') + self.assertEqual(p1.last, 'Doe') + + def test_creation_with_some_keywords(self): + Person = NamedTuple('Person', 'age first last') + p1 = Person(17, first='John', last='Doe') + self.assertEqual(p1[0], 17) + self.assertEqual(p1[1], 'John') + self.assertEqual(p1[2], 'Doe') + self.assertEqual(p1.age, 17) + self.assertEqual(p1.first, 'John') + self.assertEqual(p1.last, 'Doe') + p1 = Person(17, last='Doe', first='John') + self.assertEqual(p1[0], 17) + self.assertEqual(p1[1], 'John') + self.assertEqual(p1[2], 'Doe') + self.assertEqual(p1.age, 17) + self.assertEqual(p1.first, 'John') + self.assertEqual(p1.last, 'Doe') + + def test_custom_new(self): + class Book(NamedTuple): + title = 0 + author = 1 + genre = 2 + def __new__(cls, string): + args = [s.strip() for s in string.split(';')] + return super(Book, cls).__new__(cls, *tuple(args)) + b1 = Book('The Last Mohican; John Doe; Historical') + self.assertEqual(b1.title, 'The Last Mohican') + self.assertEqual(b1.author, 'John Doe') + self.assertEqual(b1.genre, 'Historical') + + def test_defaults_in_class(self): + class Character(NamedTuple): + name = 0 + gender = 1, None, 'male' + klass = 2, None, 'fighter' + for char in ( + {'name':'John Doe'}, + {'name':'William Pickney', 'klass':'scholar'}, + {'name':'Sarah Doughtery', 'gender':'female'}, + {'name':'Sissy Moonbeam', 'gender':'female', 'klass':'sorceress'}, + ): + c = Character(**char) + for name, value in (('name', None), ('gender','male'), ('klass','fighter')): + if name in char: + value = char[name] + self.assertEqual(getattr(c, name), value) + + def test_defaults_in_class_that_are_falsey(self): + class Point(NamedTuple): + x = 0, 'horizondal coordinate', 0 + y = 1, 'vertical coordinate', 0 + p = Point() + self.assertEqual(p.x, 0) + self.assertEqual(p.y, 0) + + def test_pickle_namedtuple_with_module(self): + if isinstance(LifeForm, Exception): + raise LifeForm + lf = LifeForm('this', 'that', 'theother') + test_pickle_dump_load(self.assertEqual, lf) + + def test_pickle_namedtuple_without_module(self): + if isinstance(DeathForm, Exception): + raise DeathForm + df = DeathForm('sickly green', '2x4', 'foul') + test_pickle_dump_load(self.assertEqual, df) + + def test_subclassing(self): + if isinstance(ThatsIt, Exception): + raise ThatsIt + ti = ThatsIt('Henry', 'Weinhardt') + self.assertEqual(ti.blah, 'Henry') + self.assertTrue(ti.what(), 'Henry') + test_pickle_dump_load(self.assertEqual, ti) + + def test_contains(self): + Book = NamedTuple('Book', 'title author genre') + b = Book('Teckla', 'Steven Brust', 'fantasy') + self.assertTrue('Teckla' in b) + self.assertTrue('Steven Brust' in b) + self.assertTrue('fantasy' in b) + + def test_fixed_size(self): + class Book(NamedTuple): + _size_ = TupleSize.fixed + title = 0 + author = 1 + genre = 2 + b = Book('Teckla', 'Steven Brust', 'fantasy') + self.assertTrue('Teckla' in b) + self.assertTrue('Steven Brust' in b) + self.assertTrue('fantasy' in b) + self.assertEqual(b.title, 'Teckla') + self.assertEqual(b.author, 'Steven Brust') + self.assertRaises(TypeError, Book, 'Teckla', 'Steven Brust') + self.assertRaises(TypeError, Book, 'Teckla') + + def test_minimum_size(self): + class Book(NamedTuple): + _size_ = TupleSize.minimum + title = 0 + author = 1 + b = Book('Teckla', 'Steven Brust', 'fantasy') + self.assertTrue('Teckla' in b) + self.assertTrue('Steven Brust' in b) + self.assertTrue('fantasy' in b) + self.assertEqual(b.title, 'Teckla') + self.assertEqual(b.author, 'Steven Brust') + b = Book('Teckla', 'Steven Brust') + self.assertTrue('Teckla' in b) + self.assertTrue('Steven Brust' in b) + self.assertEqual(b.title, 'Teckla') + self.assertEqual(b.author, 'Steven Brust') + self.assertRaises(TypeError, Book, 'Teckla') + + def test_variable_size(self): + class Book(NamedTuple): + _size_ = TupleSize.variable + title = 0 + author = 1 + genre = 2 + b = Book('Teckla', 'Steven Brust', 'fantasy') + self.assertTrue('Teckla' in b) + self.assertTrue('Steven Brust' in b) + self.assertTrue('fantasy' in b) + self.assertEqual(b.title, 'Teckla') + self.assertEqual(b.author, 'Steven Brust') + self.assertEqual(b.genre, 'fantasy') + b = Book('Teckla', 'Steven Brust') + self.assertTrue('Teckla' in b) + self.assertTrue('Steven Brust' in b) + self.assertEqual(b.title, 'Teckla') + self.assertEqual(b.author, 'Steven Brust') + self.assertRaises(AttributeError, getattr, b, 'genre') + self.assertRaises(TypeError, Book, title='Teckla', genre='fantasy') + self.assertRaises(TypeError, Book, author='Steven Brust') + + def test_combining_namedtuples(self): + class Point(NamedTuple): + x = 0, 'horizontal coordinate', 1 + y = 1, 'vertical coordinate', -1 + class Color(NamedTuple): + r = 0, 'red component', 11 + g = 1, 'green component', 29 + b = 2, 'blue component', 37 + Pixel1 = NamedTuple('Pixel', Point+Color, module=__name__) + class Pixel2(Point, Color): + "a colored dot" + class Pixel3(Point): + r = 2, 'red component', 11 + g = 3, 'green component', 29 + b = 4, 'blue component', 37 + self.assertEqual(Pixel1._fields_, 'x y r g b'.split()) + self.assertEqual(Pixel1.x.__doc__, 'horizontal coordinate') + self.assertEqual(Pixel1.x.default, 1) + self.assertEqual(Pixel1.y.__doc__, 'vertical coordinate') + self.assertEqual(Pixel1.y.default, -1) + self.assertEqual(Pixel1.r.__doc__, 'red component') + self.assertEqual(Pixel1.r.default, 11) + self.assertEqual(Pixel1.g.__doc__, 'green component') + self.assertEqual(Pixel1.g.default, 29) + self.assertEqual(Pixel1.b.__doc__, 'blue component') + self.assertEqual(Pixel1.b.default, 37) + self.assertEqual(Pixel2._fields_, 'x y r g b'.split()) + self.assertEqual(Pixel2.x.__doc__, 'horizontal coordinate') + self.assertEqual(Pixel2.x.default, 1) + self.assertEqual(Pixel2.y.__doc__, 'vertical coordinate') + self.assertEqual(Pixel2.y.default, -1) + self.assertEqual(Pixel2.r.__doc__, 'red component') + self.assertEqual(Pixel2.r.default, 11) + self.assertEqual(Pixel2.g.__doc__, 'green component') + self.assertEqual(Pixel2.g.default, 29) + self.assertEqual(Pixel2.b.__doc__, 'blue component') + self.assertEqual(Pixel2.b.default, 37) + self.assertEqual(Pixel3._fields_, 'x y r g b'.split()) + self.assertEqual(Pixel3.x.__doc__, 'horizontal coordinate') + self.assertEqual(Pixel3.x.default, 1) + self.assertEqual(Pixel3.y.__doc__, 'vertical coordinate') + self.assertEqual(Pixel3.y.default, -1) + self.assertEqual(Pixel3.r.__doc__, 'red component') + self.assertEqual(Pixel3.r.default, 11) + self.assertEqual(Pixel3.g.__doc__, 'green component') + self.assertEqual(Pixel3.g.default, 29) + self.assertEqual(Pixel3.b.__doc__, 'blue component') + self.assertEqual(Pixel3.b.default, 37) + + def test_function_api_type(self): + class Tester(NamedTuple): + def howdy(self): + return 'backwards', list(reversed(self)) + Testee = NamedTuple('Testee', 'a c e', type=Tester) + t = Testee(1, 2, 3) + self.assertEqual(t.howdy(), ('backwards', [3, 2, 1])) + + def test_asdict(self): + class Point(NamedTuple): + x = 0, 'horizontal coordinate', 1 + y = 1, 'vertical coordinate', -1 + class Color(NamedTuple): + r = 0, 'red component', 11 + g = 1, 'green component', 29 + b = 2, 'blue component', 37 + Pixel = NamedTuple('Pixel', Point+Color, module=__name__) + pixel = Pixel(99, -101, 255, 128, 0) + self.assertEqual(pixel._asdict(), {'x':99, 'y':-101, 'r':255, 'g':128, 'b':0}) + + def test_make(self): + class Point(NamedTuple): + x = 0, 'horizontal coordinate', 1 + y = 1, 'vertical coordinate', -1 + self.assertEqual(Point(4, 5), (4, 5)) + self.assertEqual(Point._make((4, 5)), (4, 5)) + + def test_replace(self): + class Color(NamedTuple): + r = 0, 'red component', 11 + g = 1, 'green component', 29 + b = 2, 'blue component', 37 + purple = Color(127, 0, 127) + mid_gray = purple._replace(g=127) + self.assertEqual(mid_gray, (127, 127, 127)) + + +class TestNamedConstant(TestCase): + + def test_constantness(self): + class K(NamedConstant): + PI = 3.141596 + TAU = 2 * PI + self.assertEqual(K.PI, 3.141596) + self.assertEqual(K.TAU, 2 * K.PI) + with self.assertRaisesRegex(AttributeError, r'cannot rebind constant'): + K.PI = 9 + with self.assertRaisesRegex(AttributeError, r'cannot delete constant'): + del K.PI + with self.assertRaisesRegex(AttributeError, r'cannot rebind constant'): + K('PI', 3) + self.assertTrue(K.PI in K) + self.assertTrue(K.TAU in K) + + def test_duplicates(self): + class CardNumber(NamedConstant): + ACE = 11 + TWO = 2 + THREE = 3 + FOUR = 4 + FIVE = 5 + SIX = 6 + SEVEN = 7 + EIGHT = 8 + NINE = 9 + TEN = 10 + JACK = 10 + QUEEN = 10 + KING = 10 + self.assertFalse(CardNumber.TEN is CardNumber.JACK) + self.assertEqual(CardNumber.TEN, CardNumber.JACK) + self.assertEqual(CardNumber.TEN, 10) + + def test_extend_constants(self): + class CardSuit(NamedConstant): + HEARTS = 1 + SPADES = 2 + DIAMONTS = 3 + CLUBS = 4 + self.assertEqual(CardSuit.HEARTS, 1) + stars = CardSuit('STARS', 5) + self.assertIs(stars, CardSuit.STARS) + self.assertEqual(CardSuit.STARS, 5) + self.assertTrue(CardSuit.STARS in CardSuit) + + def test_constant_with_docstring(self): + class Stuff(NamedConstant): + Artifact = constant(7, "lucky number!") + Bowling = 11 + HillWomp = constant(29, 'blah blah') + self.assertEqual(Stuff.Artifact, 7) + self.assertEqual(Stuff.Artifact.__doc__, 'lucky number!') + self.assertEqual(Stuff.Bowling, 11) + self.assertEqual(Stuff.Bowling.__doc__, None) + self.assertEqual(Stuff.HillWomp, 29) + self.assertEqual(Stuff.HillWomp.__doc__, 'blah blah') + + def test_deep_copy(self): + import copy + class APITypes(aenum.Constant): + STRING = "string" + INT = "int" + APITypes('string') + d = {"first": APITypes.STRING} + copy.deepcopy(d) + self.assertTrue(d['first'] is APITypes.STRING) + + def test_subclass_w_same_value(self): + class Foo(aenum.Constant): + BLA = 'bla1' + ABA = 'aba1' + class Bar(aenum.Constant): + BLA = Foo.BLA + ABA = 'aba2' + self.assertEqual(Foo.BLA, Bar.BLA) + self.assertFalse(Foo.BLA is Bar.BLA) + + +class TestStarImport(TestCase): + + def test_all_exports_names(self): + scope = {} + exec('from aenum import *', scope, scope) + self.assertIn('Enum', scope) + +class TestStackoverflowAnswers(TestCase): + + def test_self_referential_directions(self): + # https://stackoverflow.com/a/64000706/208880 + class Directions(Enum): + _order_ = 'NORTH WEST SOUTH EAST' + # + NORTH = 1, 0 + WEST = 0, 1 + SOUTH = -1, 0 + EAST = 0, -1 + # + def __init__(self, x, y): + self.x = x + self.y = y + if len(self.__class__): + # make links + all = list(self.__class__) + left, right = all[0], all[-1] + self.left = left + self.right = right + left.right = self + right.left = self + # + D = Directions + self.assertEqual(D.NORTH.value, (1, 0)) + self.assertTrue(D.NORTH.left is D.WEST) + self.assertTrue(D.SOUTH.right is D.WEST) + + def test_self_referential_rock_paper_scissors(self): + # https://stackoverflow.com/a/57085357/208880 + class RPS(Enum): + _order_ = 'Rock, Paper, Scissors' + # + Rock = "rock" + Paper = "paper" + Scissors = "scissors" + # + def __init__(self, value): + if len(self.__class__): + # make links + all = list(self.__class__) + first, previous = all[0], all[-1] + first.beats = self + self.beats = previous + # + self.assertTrue(RPS.Rock.beats is RPS.Scissors) + self.assertTrue(RPS.Scissors.beats is RPS.Paper) + self.assertTrue(RPS.Paper.beats is RPS.Rock) + + def test_arduino_headers(self): + # https://stackoverflow.com/q/65048495/208880 + class CHeader(Enum): + def __init_subclass__(cls, **kwds): + # write Enums to C header file + cls_name = cls.__name__ + header_path = getattr(cls, '_%s__header' % cls_name) + with open(header_path, 'w') as fh: + fh.write('initial header stuff here\n') + for enum in cls: + fh.write('#define %s %r\n' % (enum.name, enum.value)) + class Arduino(CHeader): + _order_ = 'ONE TWO' + __header = os.path.join(tempdir, 'arduino.h') + ONE = 1 + TWO = 2 + with open(os.path.join(tempdir, 'arduino.h')) as fh: + data = fh.read() + self.assertEqual(textwrap.dedent("""\ + initial header stuff here + #define ONE 1 + #define TWO 2 + """), + data, + ) + + def test_lowercase_compare(self): + # https://stackoverflow.com/q/65139026/208880 + class CompareLowerCase(Enum): + def __init_subclass__(cls, **kwds): + super(CompareLowerCase, cls).__init_subclass__(**kwds) + cls.lowered_names = set([m.name.lower() for m in cls]) + @classmethod + def has_name(cls, name): + return name.lower() in cls.lowered_names + # + class LabelEnum(CompareLowerCase, StrEnum): + ENUM_ONE = "Enum One" + ENUM_TWO = "Enum Two" + ENUM_THREE = "Enum Three" + FOUR = "FOUR" + FIVE = "FIVE" + SIX = "SIX" + # + self.assertTrue(LabelEnum.has_name('Enum_Three')) + + +class TestExtendEnum(TestCase): + + def test_extend_enum_plain(self): + class Color(Enum): + red = 1 + green = 2 + blue = 3 + self.assertRaisesRegex(TypeError, 'already in use as', extend_enum, Color, 'blue', 5) + # + extend_enum(Color, 'brown', 4) + self.assertEqual(Color.brown.name, 'brown') + self.assertEqual(Color.brown.value, 4) + self.assertTrue(Color.brown in Color) + self.assertEqual(Color(4), Color.brown) + self.assertEqual(Color['brown'], Color.brown) + self.assertEqual(len(Color), 4) + # + extend_enum(Color, 'mauve') + self.assertEqual(Color.mauve.name, 'mauve') + self.assertEqual(Color.mauve.value, 5) + self.assertTrue(Color.mauve in Color) + self.assertEqual(Color(5), Color.mauve) + self.assertEqual(Color['mauve'], Color.mauve) + self.assertEqual(len(Color), 5) + + def test_extend_enum_alias(self): + class Color(Enum): + red = 1 + green = 2 + blue = 3 + extend_enum(Color, 'rojo', 1) + self.assertEqual(Color.rojo.name, 'red') + self.assertEqual(Color.rojo.value, 1) + self.assertTrue(Color.rojo in Color) + self.assertEqual(Color(1), Color.rojo) + self.assertEqual(Color['rojo'], Color.red) + self.assertEqual(len(Color), 3) + + def test_extend_enum_unique(self): + class Color(UniqueEnum): + red = 1 + green = 2 + blue = 3 + self.assertRaisesRegex(ValueError, r' is a duplicate of ', extend_enum, Color, 'rojo', 1) + # + self.assertEqual(Color.red.name, 'red') + self.assertEqual(Color.red.value, 1) + self.assertTrue(Color.red in Color) + self.assertEqual(Color(1), Color.red) + self.assertEqual(Color['red'], Color.red) + self.assertEqual(Color.green.name, 'green') + self.assertEqual(Color.green.value, 2) + self.assertTrue(Color.green in Color) + self.assertEqual(Color(2), Color.green) + self.assertEqual(Color['blue'], Color.blue) + self.assertEqual(Color.blue.name, 'blue') + self.assertEqual(Color.blue.value, 3) + self.assertTrue(Color.blue in Color) + self.assertEqual(Color(3), Color.blue) + self.assertEqual(len(Color), 3) + # + extend_enum(Color, 'brown', 4) + self.assertEqual(Color.brown.name, 'brown') + self.assertEqual(Color.brown.value, 4) + self.assertTrue(Color.brown in Color) + self.assertEqual(Color(4), Color.brown) + self.assertEqual(Color['brown'], Color.brown) + self.assertEqual(len(Color), 4) + # + self.assertRaisesRegex(ValueError, '', extend_enum, Color, 'verde', 2) + # + extend_enum(Color, 'mauve') + self.assertEqual(Color.mauve.name, 'mauve') + self.assertEqual(Color.mauve.value, 5) + self.assertTrue(Color.mauve in Color) + self.assertEqual(Color(5), Color.mauve) + self.assertEqual(Color['mauve'], Color.mauve) + self.assertEqual(len(Color), 5) + + + def test_extend_enum_shadow_property(self): + class Color(Enum): + red = 1 + green = 2 + blue = 3 + extend_enum(Color, 'value', 4) + self.assertEqual(Color.value.name, 'value') + self.assertEqual(Color.value.value, 4) + self.assertTrue(Color.value in Color) + self.assertEqual(Color(4), Color.value) + self.assertEqual(Color['value'], Color.value) + self.assertEqual(len(Color), 4) + self.assertEqual(Color.red.value, 1) + + def test_extend_enum_shadow_base(self): + class hohum(object): + def cyan(self): + "cyanize a color" + return self.value + class Color(hohum, Enum): + red = 1 + green = 2 + blue = 3 + self.assertRaisesRegex(TypeError, r'already in use in superclass', extend_enum, Color, 'cyan', 4) + self.assertEqual(len(Color), 3) + self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) + + def test_extend_enum_multivalue(self): + class Color(MultiValueEnum): + red = 1, 4, 7 + green = 2, 5, 8 + blue = 3, 6, 9 + extend_enum(Color, 'brown', 10, 20) + self.assertEqual(Color.brown.name, 'brown') + self.assertEqual(Color.brown.value, 10) + self.assertTrue(Color.brown in Color) + self.assertEqual(Color(10), Color.brown) + self.assertEqual(Color(20), Color.brown) + self.assertEqual(Color['brown'], Color.brown) + self.assertEqual(len(Color), 4) + # + self.assertRaisesRegex(ValueError, 'no values specified for MultiValue enum', extend_enum, Color, 'mauve') + + def test_extend_enum_multivalue_alias(self): + class Color(MultiValueEnum): + red = 1, 4, 7 + green = 2, 5, 8 + blue = 3, 6, 9 + self.assertRaisesRegex(ValueError, r' is a duplicate of ', extend_enum, Color, 'rojo', 7) + self.assertEqual(Color.red.name, 'red') + self.assertEqual(Color.red.value, 1) + self.assertTrue(Color.red in Color) + self.assertEqual(Color(1), Color.red) + self.assertEqual(Color(4), Color.red) + self.assertEqual(Color(7), Color.red) + self.assertEqual(Color['red'], Color.red) + self.assertEqual(Color.green.name, 'green') + self.assertEqual(Color.green.value, 2) + self.assertTrue(Color.green in Color) + self.assertEqual(Color(2), Color.green) + self.assertEqual(Color(5), Color.green) + self.assertEqual(Color(8), Color.green) + self.assertEqual(Color['blue'], Color.blue) + self.assertEqual(Color.blue.name, 'blue') + self.assertEqual(Color.blue.value, 3) + self.assertTrue(Color.blue in Color) + self.assertEqual(Color(3), Color.blue) + self.assertEqual(Color(6), Color.blue) + self.assertEqual(Color(9), Color.blue) + self.assertEqual(len(Color), 3) + + def test_extend_enum_multivalue_str(self): + class M(str, MultiValueEnum): + VALUE_1 = 'value_1', 'VALUE_1' + VALUE_2 = 'value_2', 'VALUE_2' + VALUE_3 = 'value_3', 'VALUE_3' + self.assertTrue(M._member_type_ is str) + extend_enum(M, 'VALUE_4', 'value_4', 'VALUE_4') + self.assertEqual(list(M), [M.VALUE_1, M.VALUE_2, M.VALUE_3, M.VALUE_4]) + self.assertTrue(M('value_4') is M.VALUE_4) + self.assertTrue(M('VALUE_4') is M.VALUE_4) + self.assertTrue(M.VALUE_4.name == 'VALUE_4') + self.assertTrue(M.VALUE_4.value == 'value_4') + + def test_extend_intenum(self): + class Index(IntEnum): + DeviceType = 0x1000 + ErrorRegister = 0x1001 + + for name, value in ( + ('ControlWord', 0x6040), + ('StatusWord', 0x6041), + ('OperationMode', 0x6060), + ): + extend_enum(Index, name, value) + + self.assertEqual(len(Index), 5) + self.assertEqual(list(Index), [Index.DeviceType, Index.ErrorRegister, Index.ControlWord, Index.StatusWord, Index.OperationMode]) + self.assertEqual(Index.DeviceType.value, 0x1000) + self.assertEqual(Index.StatusWord.value, 0x6041) + + def test_extend_multi_init(self): + try: + from http import HTTPStatus + length = len(HTTPStatus) + except ImportError: + class HTTPStatus(IntEnum): + def __new__(cls, value, phrase, description): + obj = int.__new__(cls, value) + obj._value_ = value + + obj.phrase = phrase + obj.description = description + return obj + CONTINUE = 100, 'Continue', 'Request received, please continue' + SWITCHING_PROTOCOLS = 101, 'Switching Protocols', 'Switching to new protocol; obey Upgrade header' + PROCESSING = 102, 'Processing', '' + length = 3 + extend_enum(HTTPStatus, 'BAD_SPAM', 513, 'Too greasy', 'for a train') + extend_enum(HTTPStatus, 'BAD_EGGS', 514, 'Too green', '') + self.assertEqual(len(HTTPStatus), length+2) + self.assertEqual( + list(HTTPStatus)[-2:], + [HTTPStatus.BAD_SPAM, HTTPStatus.BAD_EGGS], + ) + self.assertEqual(HTTPStatus.BAD_SPAM.value, 513) + self.assertEqual(HTTPStatus.BAD_SPAM.name, 'BAD_SPAM') + self.assertEqual(HTTPStatus.BAD_SPAM.phrase, 'Too greasy') + self.assertEqual(HTTPStatus.BAD_SPAM.description, 'for a train') + self.assertEqual(HTTPStatus.BAD_EGGS.value, 514) + self.assertEqual(HTTPStatus.BAD_EGGS.name, 'BAD_EGGS') + self.assertEqual(HTTPStatus.BAD_EGGS.phrase, 'Too green') + self.assertEqual(HTTPStatus.BAD_EGGS.description, '') + + def test_extend_flag(self): + class Color(Flag): + BLACK = 0 + RED = 1 + GREEN = 2 + BLUE = 4 + extend_enum(Color, 'MAGENTA') + self.assertTrue(Color(8) is Color.MAGENTA) + self.assertTrue(isinstance(Color.MAGENTA, Color)) + self.assertEqual(Color.MAGENTA.value, 8) + extend_enum(Color, 'PURPLE', 11) + self.assertTrue(Color(11) is Color.PURPLE) + self.assertTrue(isinstance(Color.PURPLE, Color)) + self.assertEqual(Color.PURPLE.value, 11) + self.assertTrue(issubclass(Color, Flag)) + + def test_extend_flag_backwards(self): + class Color(Flag): + BLACK = 0 + RED = 1 + GREEN = 2 + BLUE = 4 + extend_enum(Color, 'PURPLE', 11) + self.assertTrue(Color(11) is Color.PURPLE) + self.assertTrue(isinstance(Color.PURPLE, Color)) + self.assertEqual(Color.PURPLE.value, 11) + self.assertTrue(issubclass(Color, Flag)) + # + extend_enum(Color, 'MAGENTA') + self.assertTrue(Color(8) is Color.MAGENTA) + self.assertTrue(isinstance(Color.MAGENTA, Color)) + self.assertEqual(Color.MAGENTA.value, 8) + # + extend_enum(Color, 'mauve') + self.assertEqual(Color.mauve.name, 'mauve') + self.assertEqual(Color.mauve.value, 16) + self.assertTrue(Color.mauve in Color) + self.assertEqual(Color(16), Color.mauve) + self.assertEqual(Color['mauve'], Color.mauve) + self.assertEqual(len(Color), 5) + + def test_extend_intflag(self): + class Color(IntFlag): + BLACK = 0 + RED = 1 + GREEN = 2 + BLUE = 4 + extend_enum(Color, 'MAGENTA') + self.assertTrue(Color(8) is Color.MAGENTA) + self.assertTrue(isinstance(Color.MAGENTA, Color)) + self.assertEqual(Color.MAGENTA.value, 8) + extend_enum(Color, 'PURPLE', 11) + self.assertTrue(Color(11) is Color.PURPLE) + self.assertTrue(isinstance(Color.PURPLE, Color)) + self.assertEqual(Color.PURPLE.value, 11) + self.assertTrue(issubclass(Color, Flag)) + # + extend_enum(Color, 'mauve') + self.assertEqual(Color.mauve.name, 'mauve') + self.assertEqual(Color.mauve.value, 16) + self.assertTrue(Color.mauve in Color) + self.assertEqual(Color(16), Color.mauve) + self.assertEqual(Color['mauve'], Color.mauve) + self.assertEqual(len(Color), 5) + + def test_extend_intflag_backwards(self): + class Color(IntFlag): + BLACK = 0 + RED = 1 + GREEN = 2 + BLUE = 4 + extend_enum(Color, 'PURPLE', 11) + self.assertTrue(Color(11) is Color.PURPLE) + self.assertTrue(isinstance(Color.PURPLE, Color)) + self.assertEqual(Color.PURPLE.value, 11) + self.assertTrue(issubclass(Color, Flag)) + # + extend_enum(Color, 'MAGENTA') + self.assertTrue(Color(8) is Color.MAGENTA) + self.assertTrue(isinstance(Color.MAGENTA, Color)) + self.assertEqual(Color.MAGENTA.value, 8) + # + extend_enum(Color, 'mauve') + self.assertEqual(Color.mauve.name, 'mauve') + self.assertEqual(Color.mauve.value, 16) + self.assertTrue(Color.mauve in Color) + self.assertEqual(Color(16), Color.mauve) + self.assertEqual(Color['mauve'], Color.mauve) + self.assertEqual(len(Color), 5) + + def test_extend_strenum(self): + class Color(StrEnum): + RED = auto() + GREEN = auto() + BLUE = auto() + extend_enum(Color, 'BLACK') + self.assertEqual(Color.BLACK.name, 'BLACK') + self.assertEqual(Color.BLACK.value, 'black') + self.assertEqual(len(Color), 4) + + +class TestIssues(TestCase): + + def test_auto_multi_int(self): + class Measurement(int, MultiValueEnum, AddValueEnum): + _order_ = 'one two three' + _start_ = 0 + one = "20110721" + two = "20120911" + three = "20110518" + self.assertEqual([m.value for m in Measurement], [0, 1, 2]) + self.assertEqual([m.name for m in Measurement], ['one', 'two', 'three']) + self.assertIs(Measurement('20110721'), Measurement.one) + self.assertIs(Measurement(0), Measurement.one) + self.assertIs(Measurement('20120911'), Measurement.two) + self.assertIs(Measurement(1), Measurement.two) + self.assertIs(Measurement('20110518'), Measurement.three) + self.assertIs(Measurement(2), Measurement.three) + + def test_auto_kwds(self): + class Item(Enum): + _order_ = 'A B' + A = auto(size=100, requirements={}) + B = auto(size=200, requirements={A: 1}) + # + def __new__(cls, value, size, requirements): + obj = object.__new__(cls) + obj._value_ = value + obj.size = size + # fix requirements + new_requirements = {} + for k, v in requirements.items(): + if isinstance(k, auto): + k = k.enum_member + new_requirements[k] = v + obj.requirements = new_requirements + return obj + self.assertEqual((Item.A.value, Item.A.size, Item.A.requirements), (1, 100, {})) + self.assertEqual((Item.B.value, Item.B.size, Item.B.requirements), (2, 200, {Item.A: 1})) + + def test_extend_flag(self): + class FlagTest(Flag): # Or IntFlag + NONE = 0 + LOW = 1 + MID = 2 + extend_enum(FlagTest, 'HIGH', 4) + self.assertEqual(FlagTest.LOW | FlagTest.HIGH, FlagTest(5)) + self.assertEqual((FlagTest.LOW | FlagTest.HIGH).value, 5) + + def test_extend_unhashable(self): + class TestEnum(Enum): + ABC = { + 'id': 0, + 'value': 'abc' + } + DEF = { + 'id': 1, + 'value': 'def' + } + rand = uuid.uuid4().hex + new_value = { + 'id': 99, + 'value': 'new', + } + extend_enum(TestEnum, rand, new_value) + + + +# Test conversion of global constants +# These are unordered here on purpose to ensure that declaration order +# makes no difference. +CONVERT_TEST_NAME_D = 5 +CONVERT_TEST_NAME_C = 5 +CONVERT_TEST_NAME_B = 5 +CONVERT_TEST_NAME_A = 5 # This one should sort first. +CONVERT_TEST_NAME_E = 5 +CONVERT_TEST_NAME_F = 5 +CONVERT_TEST_SIGABRT = 4 # and this one +CONVERT_TEST_SIGIOT = 4 +CONVERT_TEST_EIO = 7 +CONVERT_TEST_EBUS = 7 # and this one + +CONVERT_STRING_TEST_NAME_D = 5 +CONVERT_STRING_TEST_NAME_C = 5 +CONVERT_STRING_TEST_NAME_B = 5 +CONVERT_STRING_TEST_NAME_A = 5 # This one should sort first. +CONVERT_STRING_TEST_NAME_E = 5 +CONVERT_STRING_TEST_NAME_F = 5 + +# global names for StrEnum._convert_ test +CONVERT_STR_TEST_2 = 'goodbye' +CONVERT_STR_TEST_1 = 'hello' + +# We also need values that cannot be compared: +UNCOMPARABLE_A = 5 +UNCOMPARABLE_C = (9, 1) # naming order is broken on purpose +UNCOMPARABLE_B = 'value' + +COMPLEX_C = 1j +COMPLEX_A = 2j +COMPLEX_B = 3j + + +class TestConvert(TestCase): + + def tearDown(self): + # Reset the module-level test variables to their original integer + # values, otherwise the already created enum values get converted + # instead. + g = globals() + for suffix in ['A', 'B', 'C', 'D', 'E', 'F']: + g['CONVERT_TEST_NAME_%s' % suffix] = 5 + g['CONVERT_STRING_TEST_NAME_%s' % suffix] = 5 + for suffix, value in (('A', 5), ('B', (9, 1)), ('C', 'value')): + g['UNCOMPARABLE_%s' % suffix] = value + for suffix, value in (('A', 2j), ('B', 3j), ('C', 1j)): + g['COMPLEX_%s' % suffix] = value + for suffix, value in (('1', 'hello'), ('2', 'goodbye')): + g['CONVERT_STR_TEST_%s' % suffix] = value + g['CONVERT_TEST_SIGABRT'] = 4 + g['CONVERT_TEST_SIGIOT'] = 4 + g['CONVERT_TEST_EIO'] = 7 + g['CONVERT_TEST_EBUS'] = 7 + + def test_convert_value_lookup_priority(self): + test_type = IntEnum._convert_( + 'UnittestConvert', + MODULE, + filter=lambda x: x.startswith('CONVERT_TEST_')) + # We don't want the reverse lookup value to vary when there are + # multiple possible names for a given value. It should always + # report the first lexigraphical name in that case. + self.assertEqual(test_type(5).name, 'CONVERT_TEST_NAME_A') + self.assertEqual(test_type(4).name, 'CONVERT_TEST_SIGABRT') + self.assertEqual(test_type(7).name, 'CONVERT_TEST_EBUS') + self.assertEqual( + list(test_type), + [ + test_type.CONVERT_TEST_SIGABRT, + test_type.CONVERT_TEST_NAME_A, + test_type.CONVERT_TEST_EBUS, + ], + ) + + def test_convert_int(self): + test_type = IntEnum._convert_( + 'UnittestConvert', + MODULE, + filter=lambda x: x.startswith('CONVERT_TEST_')) + # Ensure that test_type has all of the desired names and values. + self.assertEqual(test_type.CONVERT_TEST_NAME_F, + test_type.CONVERT_TEST_NAME_A) + self.assertEqual(test_type.CONVERT_TEST_NAME_B, 5) + self.assertEqual(test_type.CONVERT_TEST_NAME_C, 5) + self.assertEqual(test_type.CONVERT_TEST_NAME_D, 5) + self.assertEqual(test_type.CONVERT_TEST_NAME_E, 5) + # Ensure that test_type only picked up names matching the filter. + int_dir = dir(int) + [ + 'CONVERT_TEST_NAME_A', 'CONVERT_TEST_NAME_B', 'CONVERT_TEST_NAME_C', + 'CONVERT_TEST_NAME_D', 'CONVERT_TEST_NAME_E', 'CONVERT_TEST_NAME_F', + 'CONVERT_TEST_SIGABRT', 'CONVERT_TEST_SIGIOT', + 'CONVERT_TEST_EIO', 'CONVERT_TEST_EBUS', + ] + extra = [name for name in dir(test_type) if name not in enum_dir(test_type)] + missing = [name for name in enum_dir(test_type) if name not in dir(test_type)] + self.assertEqual( + extra + missing, + [], + msg='extra names: %r; missing names: %r' % (extra, missing), + ) + + @unittest.skipUnless(PY3, 'everything is comparable on Python 2') + def test_convert_uncomparable(self): + uncomp = Enum._convert_( + 'Uncomparable', + MODULE, + filter=lambda x: x.startswith('UNCOMPARABLE_')) + # Should be ordered by `name` only: + self.assertEqual( + list(uncomp), + [uncomp.UNCOMPARABLE_A, uncomp.UNCOMPARABLE_B, uncomp.UNCOMPARABLE_C], + list(uncomp), + ) + + @unittest.skipUnless(PY3, 'everything is comparable on Python 2') + def test_convert_complex(self): + uncomp = Enum._convert_( + 'Uncomparable', + MODULE, + filter=lambda x: x.startswith('COMPLEX_')) + # Should be ordered by `name` only: + self.assertEqual( + list(uncomp), + [uncomp.COMPLEX_A, uncomp.COMPLEX_B, uncomp.COMPLEX_C], + ) + + def test_convert_str(self): + test_type = StrEnum._convert_( + 'UnittestConvert', + MODULE, + filter=lambda x: x.startswith('CONVERT_STR_'), + as_global=True) + # Ensure that test_type has all of the desired names and values. + self.assertEqual(test_type.CONVERT_STR_TEST_1, 'hello') + self.assertEqual(test_type.CONVERT_STR_TEST_2, 'goodbye') + # Ensure that test_type only picked up names matching the filter. + extra = [name for name in dir(test_type) if name not in enum_dir(test_type)] + missing = [name for name in enum_dir(test_type) if name not in dir(test_type)] + self.assertEqual( + extra + missing, + [], + msg='extra names: %r; missing names: %r' % (extra, missing), + ) + self.assertEqual(repr(test_type.CONVERT_STR_TEST_1), '%s.CONVERT_STR_TEST_1' % SHORT_MODULE) + self.assertEqual(str(test_type.CONVERT_STR_TEST_2), 'goodbye') + self.assertEqual(format(test_type.CONVERT_STR_TEST_1), 'hello') + + def test_convert_repr_and_str(self): + test_type = IntEnum._convert_( + 'UnittestConvert', + MODULE, + filter=lambda x: x.startswith('CONVERT_STRING_TEST_'), + as_global=True) + self.assertEqual(repr(test_type.CONVERT_STRING_TEST_NAME_A), '%s.CONVERT_STRING_TEST_NAME_A' % SHORT_MODULE) + self.assertEqual(str(test_type.CONVERT_STRING_TEST_NAME_A), '5') + self.assertEqual(format(test_type.CONVERT_STRING_TEST_NAME_A), '5') + +# helpers + +def enum_dir(cls): + interesting = set(cls._member_names_ + [ + '__class__', '__contains__', '__doc__', '__getitem__', + '__iter__', '__len__', '__members__', '__module__', + '__name__', + ]) + if cls._new_member_ is not object.__new__: + interesting.add('__new__') + if cls.__init_subclass__ is not Enum.__init_subclass__: + interesting.add('__init_subclass__') + if hasattr(object, '__qualname__'): + interesting.add('__qualname__') + for method in ('__init__', '__format__', '__repr__', '__str__'): + if getattr(cls, method) not in (getattr(Enum, method), getattr(Flag, method)): + interesting.add(method) + if cls._member_type_ is object: + return sorted(interesting) + else: + # return whatever mixed-in data type has + return sorted(set(dir(cls._member_type_)) | interesting) + +def member_dir(member): + if member.__class__._member_type_ is object: + allowed = set(['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'value']) + else: + allowed = set(dir(member)) + for cls in member.__class__.mro(): + for name, obj in cls.__dict__.items(): + if name[0] == '_': + continue + if isinstance(obj, enum.property): + if obj.fget is not None or name not in member._member_map_: + allowed.add(name) + else: + allowed.discard(name) + else: + allowed.add(name) + return sorted(allowed) + + + +if __name__ == '__main__': + tempdir = tempfile.mkdtemp() + test = None + try: + if PY3: + test_v3.tempdir = tempdir + test = unittest.main(exit=False) + sys.stdout.flush() + for name, reason in test.result.skipped: + print("%s: %s" % (name, reason)) + finally: + shutil.rmtree(tempdir, True) + if test: + sys.exit(len(test.result.errors or test.result.failures) and 1 or 0) + diff --git a/venv/Lib/site-packages/aenum/test_v3.py b/venv/Lib/site-packages/aenum/test_v3.py new file mode 100644 index 00000000..62453df1 --- /dev/null +++ b/venv/Lib/site-packages/aenum/test_v3.py @@ -0,0 +1,1982 @@ +from . import EnumMeta, Enum, IntEnum, Flag, IntFlag, StrEnum, UniqueEnum, AutoEnum, AddValueEnum +from . import NamedTuple, TupleSize, MagicValue, AddValue, NoAlias, Unique, MultiValue +from . import AutoNumberEnum,MultiValueEnum, OrderedEnum, unique, skip, extend_enum, auto +from . import StdlibEnumMeta, StdlibEnum, StdlibIntEnum, StdlibFlag, StdlibIntFlag, StdlibStrEnum +from . import pyver, PY3_3, PY3_4, PY3_5, PY3_6, PY3_11 +from . import add_stdlib_integration, remove_stdlib_integration + +from collections import OrderedDict +from datetime import timedelta +from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL +from unittest import TestCase, main + +import os +import sys +import tempfile +import textwrap +import unittest + +try: + import pyparsing +except (ImportError, SyntaxError): + pyparsing = None + +try: + RecursionError +except NameError: + # python3.4 + RecursionError = RuntimeError + +class TestEnumV3(TestCase): + + def setUp(self): + class Season(Enum): + SPRING = 1 + SUMMER = 2 + AUTUMN = 3 + WINTER = 4 + self.Season = Season + + class Konstants(float, Enum): + E = 2.7182818 + PI = 3.1415926 + TAU = 2 * PI + self.Konstants = Konstants + + class Grades(IntEnum): + A = 5 + B = 4 + C = 3 + D = 2 + F = 0 + self.Grades = Grades + + class Directional(str, Enum): + EAST = 'east' + WEST = 'west' + NORTH = 'north' + SOUTH = 'south' + self.Directional = Directional + + from datetime import date + class Holiday(date, Enum): + NEW_YEAR = 2013, 1, 1 + IDES_OF_MARCH = 2013, 3, 15 + self.Holiday = Holiday + + @unittest.skipUnless(StdlibEnumMeta, 'Stdlib enum not available') + def test_stdlib_inheritence(self): + # 3.4 + self.assertTrue(issubclass(self.Season, StdlibEnum)) + self.assertTrue(isinstance(self.Season.SPRING, StdlibEnum)) + # + if pyver >= PY3_6: + class AFlag(Flag): + one = 1 + self.assertTrue(issubclass(AFlag, StdlibEnum)) + self.assertTrue(isinstance(AFlag.one, StdlibEnum)) + self.assertTrue(issubclass(AFlag, StdlibFlag)) + self.assertTrue(isinstance(AFlag.one, StdlibFlag)) + # + class AnIntFlag(IntFlag): + one = 1 + self.assertTrue(issubclass(AnIntFlag, StdlibEnum)) + self.assertTrue(isinstance(AnIntFlag.one, StdlibEnum)) + self.assertTrue(issubclass(AnIntFlag, StdlibFlag)) + self.assertTrue(isinstance(AnIntFlag.one, StdlibFlag)) + self.assertTrue(issubclass(AnIntFlag, StdlibIntFlag)) + self.assertTrue(isinstance(AnIntFlag.one, StdlibIntFlag)) + # + if pyver >= PY3_11: + class AStrEnum(StrFlag): + one = '1' + self.assertTrue(issubclass(AStrEnum, StdlibEnum)) + self.assertTrue(isinstance(AStrEnum.one, StdlibEnum)) + self.assertTrue(issubclass(AStrEnum, StdlibStrEnum)) + self.assertTrue(isinstance(AStrEnum.one, StdlibStrEnum)) + + @unittest.skipUnless(StdlibEnumMeta, 'Stdlib enum not available') + def test_stdlib_bad_getattribute(self): + class BadEnumType(StdlibEnumMeta): + def __getattribute__(cls, name): + obj = super().__getattribute__(name) + if isinstance(obj, cls): + obj.deprecate() + return obj + with self.assertRaisesRegex(RecursionError, 'endless recursion'): + class BaseEnum(StdlibEnum): + pass + class BadEnum(BaseEnum, metaclass=BadEnumType): + FOO = 'bar' + try: + remove_stdlib_integration() + class OkayEnum(StdlibEnum, metaclass=BadEnumType): + FOO = 'bar' + finally: + add_stdlib_integration() + + @unittest.skipUnless(pyver >= PY3_5, '__qualname__ requires python 3.5 or greater') + def test_pickle_enum_function_with_qualname(self): + Theory = Enum('Theory', 'rule law supposition', qualname='spanish_inquisition') + globals()['spanish_inquisition'] = Theory + test_pickle_dump_load(self.assertTrue, Theory.rule) + test_pickle_dump_load(self.assertTrue, Theory) + + def test_auto_init(self): + class Planet(Enum, init='mass radius'): + MERCURY = (3.303e+23, 2.4397e6) + VENUS = (4.869e+24, 6.0518e6) + EARTH = (5.976e+24, 6.37814e6) + MARS = (6.421e+23, 3.3972e6) + JUPITER = (1.9e+27, 7.1492e7) + SATURN = (5.688e+26, 6.0268e7) + URANUS = (8.686e+25, 2.5559e7) + NEPTUNE = (1.024e+26, 2.4746e7) + @property + def surface_gravity(self): + # universal gravitational constant (m3 kg-1 s-2) + G = 6.67300E-11 + return G * self.mass / (self.radius * self.radius) + self.assertEqual(round(Planet.EARTH.surface_gravity, 2), 9.80) + self.assertEqual(Planet.EARTH.value, (5.976e+24, 6.37814e6)) + + def test_auto_init_with_value(self): + class Color(Enum, init='value, rgb'): + RED = 1, (1, 0, 0) + BLUE = 2, (0, 1, 0) + GREEN = 3, (0, 0, 1) + self.assertEqual(Color.RED.value, 1) + self.assertEqual(Color.BLUE.value, 2) + self.assertEqual(Color.GREEN.value, 3) + self.assertEqual(Color.RED.rgb, (1, 0, 0)) + self.assertEqual(Color.BLUE.rgb, (0, 1, 0)) + self.assertEqual(Color.GREEN.rgb, (0, 0, 1)) + + def test_auto_turns_off(self): + with self.assertRaises(NameError): + class Color(Enum, settings=MagicValue): + red + green + blue + def hello(self): + print('Hello! My serial is %s.' % self.value) + rose + with self.assertRaises(NameError): + class Color(Enum, settings=MagicValue): + red + green + blue + def __init__(self, *args): + pass + rose + + def test_magic(self): + class Color(Enum, settings=MagicValue): + red, green, blue + self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) + self.assertEqual(Color.red.value, 1) + + def test_ignore_not_overridden(self): + with self.assertRaisesRegex(TypeError, 'object is not callable'): + class Color(Flag): + _ignore_ = 'irrelevent' + _settings_ = MagicValue + @property + def shade(self): + print('I am light', self.name.lower()) + + def test_magic_start(self): + class Color(Enum, settings=MagicValue, start=0): + red, green, blue + self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) + self.assertEqual(Color.red.value, 0) + + def test_dir_on_class(self): + Season = self.Season + self.assertEqual( + set(dir(Season)), + set(['__class__', '__doc__', '__members__', '__module__', + 'SPRING', 'SUMMER', 'AUTUMN', 'WINTER', + '__init_subclass__', '__name__', '__getitem__', '__len__', + '__contains__', '__iter__', '__qualname__', + ])) + + def test_dir_on_item(self): + Season = self.Season + self.assertEqual( + set(dir(Season.WINTER)), + set(['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'value', 'values']), + ) + + def test_dir_with_added_behavior(self): + class Test(Enum): + this = 'that' + these = 'those' + def wowser(self): + return ("Wowser! I'm %s!" % self.name) + self.assertEqual( + set(dir(Test)), + set([ + '__class__', '__doc__', '__members__', '__module__', 'this', 'these', + '__init_subclass__', '__name__', '__getitem__', '__len__', + '__contains__', '__iter__', '__qualname__', + ])) + self.assertEqual( + set(dir(Test.this)), + set(['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'value', 'values', 'wowser']), + ) + + def test_dir_on_sub_with_behavior_on_super(self): + # see issue22506 + class SuperEnum(Enum): + def invisible(self): + return "did you see me?" + class SubEnum(SuperEnum): + sample = 5 + self.assertEqual( + set(dir(SubEnum.sample)), + set(['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'value', 'values', 'invisible']), + ) + + def test_members_are_always_ordered(self): + class AlwaysOrdered(Enum): + first = 1 + second = 2 + third = 3 + self.assertTrue(type(AlwaysOrdered.__members__) is OrderedDict) + + def test_comparisons(self): + def bad_compare(): + Season.SPRING > 4 + Season = self.Season + self.assertNotEqual(Season.SPRING, 1) + self.assertRaises(TypeError, bad_compare) + + class Part(Enum): + SPRING = 1 + CLIP = 2 + BARREL = 3 + + self.assertNotEqual(Season.SPRING, Part.SPRING) + def bad_compare(): + Season.SPRING < Part.CLIP + self.assertRaises(TypeError, bad_compare) + + def test_duplicate_name(self): + with self.assertRaises(TypeError): + class Color1(Enum): + red = 1 + green = 2 + blue = 3 + red = 4 + + with self.assertRaises(TypeError): + class Color2(Enum): + red = 1 + green = 2 + blue = 3 + def red(self): + return 'red' + + with self.assertRaises(TypeError): + class Color3(Enum): + @property + def red(self): + return 'redder' + red = 1 + green = 2 + blue = 3 + + def test_duplicate_value_with_unique(self): + with self.assertRaises(ValueError): + class Color(Enum, settings=Unique): + red = 1 + green = 2 + blue = 3 + rojo = 1 + + def test_duplicate_value_with_noalias(self): + class Color(Enum, settings=NoAlias): + red = 1 + green = 2 + blue = 3 + rojo = 1 + self.assertFalse(Color.red is Color.rojo) + self.assertEqual(Color.red.value, 1) + self.assertEqual(Color.rojo.value, 1) + self.assertEqual(len(Color), 4) + self.assertEqual(list(Color), [Color.red, Color.green, Color.blue, Color.rojo]) + + def test_noalias_value_lookup(self): + class Color(Enum, settings=NoAlias): + red = 1 + green = 2 + blue = 3 + rojo = 1 + self.assertRaises(TypeError, Color, 2) + + def test_multivalue(self): + class Color(Enum, settings=MultiValue): + red = 1, 'red' + green = 2, 'green' + blue = 3, 'blue' + self.assertEqual(Color.red.value, 1) + self.assertIs(Color('green'), Color.green) + self.assertEqual(Color.blue.values, (3, 'blue')) + + def test_multivalue_with_duplicate_values(self): + with self.assertRaises(ValueError): + class Color(Enum, settings=MultiValue): + red = 1, 'red' + green = 2, 'green' + blue = 3, 'blue', 'red' + + def test_multivalue_with_duplicate_values_and_noalias(self): + with self.assertRaises(TypeError): + class Color(Enum, settings=(MultiValue, NoAlias)): + red = 1, 'red' + green = 2, 'green' + blue = 3, 'blue', 'red' + + def test_multivalue_and_auto(self): + with self.assertRaisesRegex(TypeError, r'MultiValue and MagicValue are mutually exclusive'): + class Color(Enum, settings=(MultiValue, MagicValue)): + red + green = 3, 'green' + blue + + def test_autonumber_and_init(self): + class Field(IntEnum, settings=AddValue, init='__doc__'): + TYPE = "Char, Date, Logical, etc." + START = "Field offset in record" + self.assertEqual(Field.TYPE, 1) + self.assertEqual(Field.START, 2) + self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.') + self.assertEqual(Field.START.__doc__, 'Field offset in record') + self.assertFalse(hasattr(Field, '_order_')) + + def test_autovalue_and_init(self): + class Field(IntEnum, init='value __doc__'): + TYPE = "Char, Date, Logical, etc." + START = "Field offset in record" + self.assertEqual(Field.TYPE, 1) + self.assertEqual(Field.START.__doc__, 'Field offset in record') + + def test_autonumber_and_start(self): + class Field(IntEnum, init='__doc__', settings=AddValue, start=0): + TYPE = "Char, Date, Logical, etc." + START = "Field offset in record" + self.assertEqual(Field.TYPE, 0) + self.assertEqual(Field.START, 1) + self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.') + self.assertEqual(Field.START.__doc__, 'Field offset in record') + + def test_autonumber_and_init_and_some_values(self): + class Field(IntEnum, init='value __doc__'): + TYPE = "Char, Date, Logical, etc." + START = "Field offset in record" + BLAH = 5, "test blah" + BELCH = 'test belch' + self.assertEqual(Field.TYPE, 1) + self.assertEqual(Field.START, 2) + self.assertEqual(Field.BLAH, 5) + self.assertEqual(Field.BELCH, 6) + self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.') + self.assertEqual(Field.START.__doc__, 'Field offset in record') + self.assertEqual(Field.BLAH.__doc__, 'test blah') + self.assertEqual(Field.BELCH.__doc__, 'test belch') + + def test_autonumber_with_irregular_values(self): + class Point(AutoNumberEnum, init='x y'): + first = 7, 9 + second = 11, 13 + self.assertEqual(Point.first.value, 1) + self.assertEqual(Point.first.x, 7) + self.assertEqual(Point.first.y, 9) + self.assertEqual(Point.second.value, 2) + self.assertEqual(Point.second.x, 11) + self.assertEqual(Point.second.y, 13) + with self.assertRaisesRegex(TypeError, '.*number of fields provided do not match init ...x., .y.. != .3, 11, 13..'): + class Point(AutoNumberEnum, init='x y'): + first = 7, 9 + second = 3, 11, 13 + class Color(AutoNumberEnum, init='__doc__'): + # interactions between AutoNumberEnum and _generate_next_value_ may not be pretty + red = () + green = 'red' + blue = () + self.assertTrue(Color.red.__doc__, 1) + self.assertEqual(Color.green.__doc__, 'red') + self.assertTrue(Color.blue.__doc__, 2) + + def test_autonumber_and_property(self): + with self.assertRaises(TypeError): + class Color(AutoEnum): + _ignore_ = () + red = () + green = () + blue = () + @property + def cap_name(self) -> str: + return self.name.title() + + def test_autoenum(self): + class Color(AutoEnum): + red + green + blue + self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) + self.assertEqual([m.value for m in Color], [1, 2, 3]) + self.assertEqual([m.name for m in Color], ['red', 'green', 'blue']) + + def test_autoenum_with_str(self): + class Color(AutoEnum): + def _generate_next_value_(name, start, count, last_values): + return name + red + green + blue + self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) + self.assertEqual([m.value for m in Color], ['red', 'green', 'blue']) + self.assertEqual([m.name for m in Color], ['red', 'green', 'blue']) + + def test_autoenum_and_default_ignore(self): + class Color(AutoEnum): + red + green + blue + @property + def cap_name(self): + return self.name.title() + self.assertEqual(Color.blue.cap_name, 'Blue') + + def test_autonumber_and_overridden_ignore(self): + with self.assertRaises(TypeError): + class Color(AutoEnum): + _ignore_ = 'staticmethod' + red + green + blue + @property + def cap_name(self) -> str: + return self.name.title() + + def test_autonumber_and_multiple_assignment(self): + class Color(AutoEnum): + _ignore_ = 'property' + red + green + blue = cyan + @property + def cap_name(self) -> str: + return self.name.title() + self.assertEqual(Color.blue.cap_name, 'Cyan') + + def test_multivalue_and_autonumber_inherited(self): + class Measurement(int, Enum, settings=(MultiValue, AddValue), start=0): + one = "20110721" + two = "20120911" + three = "20110518" + M = Measurement + self.assertEqual(M.one, 0) + self.assertTrue(M.one is M(0) is M('20110721')) + + def test_combine_new_settings_with_old_settings(self): + class Auto(Enum, settings=Unique): + pass + with self.assertRaises(ValueError): + class AutoUnique(Auto, settings=MagicValue): + BLAH + BLUH + ICK = 1 + + def test_timedelta(self): + class Period(timedelta, Enum): + ''' + different lengths of time + ''' + _init_ = 'value period' + _settings_ = NoAlias + _ignore_ = 'Period i' + Period = vars() + for i in range(31): + Period['day_%d' % i] = i, 'day' + for i in range(15): + Period['week_%d' % i] = i*7, 'week' + for i in range(12): + Period['month_%d' % i] = i*30, 'month' + OneDay = day_1 + OneWeek = week_1 + self.assertFalse(hasattr(Period, '_ignore_')) + self.assertFalse(hasattr(Period, 'Period')) + self.assertFalse(hasattr(Period, 'i')) + self.assertTrue(isinstance(Period.day_1, timedelta)) + + def test_extend_enum_plain(self): + class Color(UniqueEnum): + red = 1 + green = 2 + blue = 3 + extend_enum(Color, 'brown', 4) + self.assertEqual(Color.brown.name, 'brown') + self.assertEqual(Color.brown.value, 4) + self.assertTrue(Color.brown in Color) + self.assertEqual(len(Color), 4) + + def test_extend_enum_shadow(self): + class Color(UniqueEnum): + red = 1 + green = 2 + blue = 3 + extend_enum(Color, 'value', 4) + self.assertEqual(Color.value.name, 'value') + self.assertEqual(Color.value.value, 4) + self.assertTrue(Color.value in Color) + self.assertEqual(len(Color), 4) + self.assertEqual(Color.red.value, 1) + + def test_extend_enum_generate(self): + class Foo(AutoEnum): + def _generate_next_value_(name, start, count, values, *args, **kwds): + return name + a + b + # + extend_enum(Foo, 'c') + self.assertEqual(Foo.a.value, 'a') + self.assertEqual(Foo.b.value, 'b') + self.assertEqual(Foo.c.value, 'c') + + def test_extend_enum_unique_with_duplicate(self): + with self.assertRaises(ValueError): + class Color(Enum, settings=Unique): + red = 1 + green = 2 + blue = 3 + extend_enum(Color, 'value', 1) + + def test_extend_enum_multivalue_with_duplicate(self): + with self.assertRaises(ValueError): + class Color(Enum, settings=MultiValue): + red = 1, 'rojo' + green = 2, 'verde' + blue = 3, 'azul' + extend_enum(Color, 'value', 2) + + def test_extend_enum_noalias_with_duplicate(self): + class Color(Enum, settings=NoAlias): + red = 1 + green = 2 + blue = 3 + extend_enum(Color, 'value', 3, ) + self.assertRaises(TypeError, Color, 3) + self.assertFalse(Color.value is Color.blue) + self.assertTrue(Color.value.value, 3) + + def test_no_duplicates(self): + def bad_duplicates(): + class Color(UniqueEnum): + red = 1 + green = 2 + blue = 3 + class Color(UniqueEnum): + red = 1 + green = 2 + blue = 3 + grene = 2 + self.assertRaises(ValueError, bad_duplicates) + + def test_no_duplicates_kinda(self): + class Silly(UniqueEnum): + one = 1 + two = 'dos' + name = 3 + class Sillier(IntEnum, UniqueEnum): + single = 1 + name = 2 + triple = 3 + value = 4 + + def test_auto_number(self): + class Color(Enum, settings=MagicValue): + red + blue + green + + self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) + self.assertEqual(Color.red.value, 1) + self.assertEqual(Color.blue.value, 2) + self.assertEqual(Color.green.value, 3) + + def test_auto_name(self): + class Color(Enum, settings=MagicValue): + def _generate_next_value_(name, start, count, last): + return name + red + blue + green + + self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) + self.assertEqual(Color.red.value, 'red') + self.assertEqual(Color.blue.value, 'blue') + self.assertEqual(Color.green.value, 'green') + + def test_auto_name_inherit(self): + class AutoNameEnum(Enum): + def _generate_next_value_(name, start, count, last): + return name + class Color(AutoNameEnum, settings=MagicValue): + red + blue + green + + self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) + self.assertEqual(Color.red.value, 'red') + self.assertEqual(Color.blue.value, 'blue') + self.assertEqual(Color.green.value, 'green') + + def test_auto_garbage(self): + class Color(Enum): + _settings_ = MagicValue + red = 'red' + blue + self.assertEqual(Color.blue.value, 1) + + def test_auto_garbage_corrected(self): + class Color(Enum, settings=MagicValue): + red = 'red' + blue = 2 + green + + self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) + self.assertEqual(Color.red.value, 'red') + self.assertEqual(Color.blue.value, 2) + self.assertEqual(Color.green.value, 3) + + def test_duplicate_auto(self): + class Dupes(Enum, settings=MagicValue): + first = primero + second + third + self.assertEqual([Dupes.first, Dupes.second, Dupes.third], list(Dupes)) + + def test_order_as_function(self): + # first with _init_ + class TestSequence(Enum): + _init_ = 'value, sequence' + _order_ = lambda member: member.sequence + item_id = 'An$(1,6)', 0 # Item Code + company_id = 'An$(7,2)', 1 # Company Code + warehouse_no = 'An$(9,4)', 2 # Warehouse Number + company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY + key_type = 'Cn$(19,3)', 4 # Key Type = '1**' + available = 'Zn$(1,1)', 5 # Available? + contract_item = 'Bn(2,1)', 6 # Contract Item? + sales_category = 'Fn', 7 # Sales Category + gl_category = 'Rn$(5,1)', 8 # G/L Category + warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category + inv_units = 'Qn$(7,2)', 10 # Inv Units + for i, member in enumerate(TestSequence): + self.assertEqual(i, member.sequence) + ts = TestSequence + self.assertEqual(ts.item_id.name, 'item_id') + self.assertEqual(ts.item_id.value, 'An$(1,6)') + self.assertEqual(ts.item_id.sequence, 0) + self.assertEqual(ts.company_id.name, 'company_id') + self.assertEqual(ts.company_id.value, 'An$(7,2)') + self.assertEqual(ts.company_id.sequence, 1) + self.assertEqual(ts.warehouse_no.name, 'warehouse_no') + self.assertEqual(ts.warehouse_no.value, 'An$(9,4)') + self.assertEqual(ts.warehouse_no.sequence, 2) + self.assertEqual(ts.company.name, 'company') + self.assertEqual(ts.company.value, 'Hn$(13,6)') + self.assertEqual(ts.company.sequence, 3) + self.assertEqual(ts.key_type.name, 'key_type') + self.assertEqual(ts.key_type.value, 'Cn$(19,3)') + self.assertEqual(ts.key_type.sequence, 4) + self.assertEqual(ts.available.name, 'available') + self.assertEqual(ts.available.value, 'Zn$(1,1)') + self.assertEqual(ts.available.sequence, 5) + self.assertEqual(ts.contract_item.name, 'contract_item') + self.assertEqual(ts.contract_item.value, 'Bn(2,1)') + self.assertEqual(ts.contract_item.sequence, 6) + self.assertEqual(ts.sales_category.name, 'sales_category') + self.assertEqual(ts.sales_category.value, 'Fn') + self.assertEqual(ts.sales_category.sequence, 7) + self.assertEqual(ts.gl_category.name, 'gl_category') + self.assertEqual(ts.gl_category.value, 'Rn$(5,1)') + self.assertEqual(ts.gl_category.sequence, 8) + self.assertEqual(ts.warehouse_category.name, 'warehouse_category') + self.assertEqual(ts.warehouse_category.value, 'Sn$(6,1)') + self.assertEqual(ts.warehouse_category.sequence, 9) + self.assertEqual(ts.inv_units.name, 'inv_units') + self.assertEqual(ts.inv_units.value, 'Qn$(7,2)') + self.assertEqual(ts.inv_units.sequence, 10) + # and then without + class TestSequence(Enum): + _order_ = lambda member: member.value[1] + item_id = 'An$(1,6)', 0 # Item Code + company_id = 'An$(7,2)', 1 # Company Code + warehouse_no = 'An$(9,4)', 2 # Warehouse Number + company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY + key_type = 'Cn$(19,3)', 4 # Key Type = '1**' + available = 'Zn$(1,1)', 5 # Available? + contract_item = 'Bn(2,1)', 6 # Contract Item? + sales_category = 'Fn', 7 # Sales Category + gl_category = 'Rn$(5,1)', 8 # G/L Category + warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category + inv_units = 'Qn$(7,2)', 10 # Inv Units + for i, member in enumerate(TestSequence): + self.assertEqual(i, member.value[1]) + ts = TestSequence + self.assertEqual(ts.item_id.name, 'item_id') + self.assertEqual(ts.item_id.value, ('An$(1,6)', 0)) + self.assertEqual(ts.company_id.name, 'company_id') + self.assertEqual(ts.company_id.value, ('An$(7,2)', 1)) + self.assertEqual(ts.warehouse_no.name, 'warehouse_no') + self.assertEqual(ts.warehouse_no.value, ('An$(9,4)', 2)) + self.assertEqual(ts.company.name, 'company') + self.assertEqual(ts.company.value, ('Hn$(13,6)', 3)) + self.assertEqual(ts.key_type.name, 'key_type') + self.assertEqual(ts.key_type.value, ('Cn$(19,3)', 4)) + self.assertEqual(ts.available.name, 'available') + self.assertEqual(ts.available.value, ('Zn$(1,1)', 5)) + self.assertEqual(ts.contract_item.name, 'contract_item') + self.assertEqual(ts.contract_item.value, ('Bn(2,1)', 6)) + self.assertEqual(ts.sales_category.name, 'sales_category') + self.assertEqual(ts.sales_category.value, ('Fn', 7)) + self.assertEqual(ts.gl_category.name, 'gl_category') + self.assertEqual(ts.gl_category.value, ('Rn$(5,1)', 8)) + self.assertEqual(ts.warehouse_category.name, 'warehouse_category') + self.assertEqual(ts.warehouse_category.value, ('Sn$(6,1)', 9)) + self.assertEqual(ts.inv_units.name, 'inv_units') + self.assertEqual(ts.inv_units.value, ('Qn$(7,2)', 10)) + # then with _init_ but without value + with self.assertRaises(TypeError): + class TestSequence(Enum): + _init_ = 'sequence' + _order_ = lambda member: member.sequence + item_id = 'An$(1,6)', 0 # Item Code + company_id = 'An$(7,2)', 1 # Company Code + warehouse_no = 'An$(9,4)', 2 # Warehouse Number + company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY + key_type = 'Cn$(19,3)', 4 # Key Type = '1**' + available = 'Zn$(1,1)', 5 # Available? + contract_item = 'Bn(2,1)', 6 # Contract Item? + sales_category = 'Fn', 7 # Sales Category + gl_category = 'Rn$(5,1)', 8 # G/L Category + warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category + inv_units = 'Qn$(7,2)', 10 # Inv Units + # finally, out of order so Python 3 barfs + with self.assertRaises(TypeError): + class TestSequence(Enum): + _init_ = 'sequence' + _order_ = lambda member: member.sequence + item_id = 'An$(1,6)', 0 # Item Code + warehouse_no = 'An$(9,4)', 2 # Warehouse Number + company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY + company_id = 'An$(7,2)', 1 # Company Code + inv_units = 'Qn$(7,2)', 10 # Inv Units + available = 'Zn$(1,1)', 5 # Available? + contract_item = 'Bn(2,1)', 6 # Contract Item? + sales_category = 'Fn', 7 # Sales Category + key_type = 'Cn$(19,3)', 4 # Key Type = '1**' + gl_category = 'Rn$(5,1)', 8 # G/L Category + warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category + + if pyver >= PY3_3: + def test_missing(self): + class Color(Enum): + red = 1 + green = 2 + blue = 3 + @classmethod + def _missing_(cls, item): + if item == 'three': + return cls.blue + elif item == 'bad return': + # trigger internal error + return 5 + elif item == 'error out': + raise ZeroDivisionError + else: + # trigger not found + return None + self.assertIs(Color('three'), Color.blue) + self.assertRaises(ValueError, Color, 7) + try: + Color('bad return') + except TypeError as exc: + self.assertTrue(isinstance(exc.__cause__, ValueError)) + else: + raise Exception('Exception not raised.') + try: + Color('error out') + except ZeroDivisionError as exc: + self.assertTrue(isinstance(exc.__cause__, ValueError)) + else: + raise Exception('Exception not raised.') + + def test_enum_of_types(self): + """Support using Enum to refer to types deliberately.""" + class MyTypes(Enum): + i = int + f = float + s = str + self.assertEqual(MyTypes.i.value, int) + self.assertEqual(MyTypes.f.value, float) + self.assertEqual(MyTypes.s.value, str) + class Foo: + pass + class Bar: + pass + class MyTypes2(Enum): + a = Foo + b = Bar + self.assertEqual(MyTypes2.a.value, Foo) + self.assertEqual(MyTypes2.b.value, Bar) + class SpamEnumNotInner: + pass + class SpamEnum(Enum): + spam = SpamEnumNotInner + self.assertEqual(SpamEnum.spam.value, SpamEnumNotInner) + + def test_nested_classes_in_enum_do_not_create_members(self): + """Support locally-defined nested classes.""" + # manually set __qualname__ to remove testing framework noise + class Outer(Enum): + __qualname__ = "Outer" + a = 1 + b = 2 + class Inner(Enum): + __qualname__ = "Outer.Inner" + foo = 10 + bar = 11 + self.assertTrue(isinstance(Outer.Inner, type)) + self.assertEqual(Outer.a.value, 1) + self.assertEqual(Outer.Inner.foo.value, 10) + self.assertEqual( + list(Outer.Inner), + [Outer.Inner.foo, Outer.Inner.bar], + ) + self.assertEqual( + list(Outer), + [Outer.a, Outer.b], + ) + + if pyver == PY3_4: + def test_class_nested_enum_and_pickle_protocol_four(self): + # would normally just have this directly in the class namespace + class NestedEnum(Enum): + twigs = 'common' + shiny = 'rare' + + self.__class__.NestedEnum = NestedEnum + self.NestedEnum.__qualname__ = '%s.NestedEnum' % self.__class__.__name__ + test_pickle_exception( + self.assertRaises, PicklingError, self.NestedEnum.twigs, + protocol=(0, 3)) + test_pickle_dump_load(self.assertTrue, self.NestedEnum.twigs, + protocol=(4, HIGHEST_PROTOCOL)) + + elif pyver >= PY3_5: + def test_class_nested_enum_and_pickle_protocol_four(self): + # would normally just have this directly in the class namespace + class NestedEnum(Enum): + twigs = 'common' + shiny = 'rare' + + self.__class__.NestedEnum = NestedEnum + self.NestedEnum.__qualname__ = '%s.NestedEnum' % self.__class__.__name__ + test_pickle_dump_load(self.assertTrue, self.NestedEnum.twigs, + protocol=(0, HIGHEST_PROTOCOL)) + + if pyver >= PY3_4: + def test_enum_injection(self): + class Color(Enum): + _order_ = 'BLACK WHITE' + BLACK = Color('black', '#000') + WHITE = Color('white', '#fff') + + def __init__(self, label, hex): + self.label = label + self.hex = hex + + self.assertEqual([Color.BLACK, Color.WHITE], list(Color)) + self.assertEqual(Color.WHITE.hex, '#fff') + self.assertEqual(Color.BLACK.label, 'black') + + def test_subclasses_with_getnewargs_ex(self): + class NamedInt(int): + __qualname__ = 'NamedInt' # needed for pickle protocol 4 + def __new__(cls, *args): + _args = args + if len(args) < 2: + raise TypeError("name and value must be specified") + name, args = args[0], args[1:] + self = int.__new__(cls, *args) + self._intname = name + self._args = _args + return self + def __getnewargs_ex__(self): + return self._args, {} + @property + def __name__(self): + return self._intname + def __repr__(self): + # repr() is updated to include the name and type info + return "{}({!r}, {})".format(type(self).__name__, + self.__name__, + int.__repr__(self)) + def __str__(self): + # str() is unchanged, even if it relies on the repr() fallback + base = int + base_str = base.__str__ + if base_str.__objclass__ is object: + return base.__repr__(self) + return base_str(self) + # for simplicity, we only define one operator that + # propagates expressions + def __add__(self, other): + temp = int(self) + int( other) + if isinstance(self, NamedInt) and isinstance(other, NamedInt): + return NamedInt( + '({0} + {1})'.format(self.__name__, other.__name__), + temp ) + else: + return temp + + class NEI(NamedInt, Enum): + __qualname__ = 'NEI' # needed for pickle protocol 4 + x = ('the-x', 1) + y = ('the-y', 2) + + + self.assertIs(NEI.__new__, Enum.__new__) + self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") + globals()['NamedInt'] = NamedInt + globals()['NEI'] = NEI + NI5 = NamedInt('test', 5) + self.assertEqual(NI5, 5) + test_pickle_dump_load(self.assertEqual, NI5, 5, protocol=(4, HIGHEST_PROTOCOL)) + self.assertEqual(NEI.y.value, 2) + test_pickle_dump_load(self.assertTrue, NEI.y, protocol=(4, HIGHEST_PROTOCOL)) + + +class TestOrderV3(TestCase): + """ + Test definition order versus _order_ order. + """ + + def test_same_members(self): + class Color(Enum): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 3 + + def test_same_members_with_aliases(self): + class Color(Enum): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 3 + verde = green + + def test_same_members_wrong_order(self): + with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): + class Color(Enum): + _order_ = 'red green blue' + red = 1 + blue = 3 + green = 2 + + def test_order_has_extra_members(self): + with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): + class Color(Enum): + _order_ = 'red green blue purple' + red = 1 + green = 2 + blue = 3 + + def test_order_has_extra_members_with_aliases(self): + with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): + class Color(Enum): + _order_ = 'red green blue purple' + red = 1 + green = 2 + blue = 3 + verde = green + + def test_enum_has_extra_members(self): + with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): + class Color(Enum): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 3 + purple = 4 + + def test_enum_has_extra_members_with_aliases(self): + with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): + class Color(Enum): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 3 + purple = 4 + verde = green + + def test_same_members_flag(self): + class Color(Flag): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 4 + + def test_same_members_with_aliases_flag(self): + class Color(Flag): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 4 + verde = green + + def test_same_members_wrong_order_falg(self): + with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): + class Color(Flag): + _order_ = 'red green blue' + red = 1 + blue = 4 + green = 2 + + def test_order_has_extra_members_flag(self): + with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): + class Color(Flag): + _order_ = 'red green blue purple' + red = 1 + green = 2 + blue = 4 + + def test_order_has_extra_members_with_aliases_flag(self): + with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): + class Color(Flag): + _order_ = 'red green blue purple' + red = 1 + green = 2 + blue = 4 + verde = green + + def test_enum_has_extra_members_flag(self): + with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): + class Color(Flag): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 4 + purple = 8 + + def test_enum_has_extra_members_with_aliases_flag(self): + with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): + class Color(Flag): + _order_ = 'red green blue' + red = 1 + green = 2 + blue = 4 + purple = 8 + verde = green + + +class TestNamedTupleV3(TestCase): + + def test_fixed_size(self): + class Book(NamedTuple, size=TupleSize.fixed): + title = 0 + author = 1 + genre = 2 + b = Book('Teckla', 'Steven Brust', 'fantasy') + self.assertTrue('Teckla' in b) + self.assertTrue('Steven Brust' in b) + self.assertTrue('fantasy' in b) + self.assertEqual(b.title, 'Teckla') + self.assertEqual(b.author, 'Steven Brust') + self.assertRaises(TypeError, Book, 'Teckla', 'Steven Brust') + self.assertRaises(TypeError, Book, 'Teckla') + + def test_minimum_size(self): + class Book(NamedTuple, size=TupleSize.minimum): + title = 0 + author = 1 + b = Book('Teckla', 'Steven Brust', 'fantasy') + self.assertTrue('Teckla' in b) + self.assertTrue('Steven Brust' in b) + self.assertTrue('fantasy' in b) + self.assertEqual(b.title, 'Teckla') + self.assertEqual(b.author, 'Steven Brust') + self.assertEqual(b[2], 'fantasy') + b = Book('Teckla', 'Steven Brust') + self.assertTrue('Teckla' in b) + self.assertTrue('Steven Brust' in b) + self.assertEqual(b.title, 'Teckla') + self.assertEqual(b.author, 'Steven Brust') + self.assertRaises(TypeError, Book, 'Teckla') + + def test_variable_size(self): + class Book(NamedTuple, size=TupleSize.variable): + title = 0 + author = 1 + genre = 2 + b = Book('Teckla', 'Steven Brust', 'fantasy') + self.assertTrue('Teckla' in b) + self.assertTrue('Steven Brust' in b) + self.assertTrue('fantasy' in b) + self.assertEqual(b.title, 'Teckla') + self.assertEqual(b.author, 'Steven Brust') + self.assertEqual(b.genre, 'fantasy') + b = Book('Teckla', 'Steven Brust') + self.assertTrue('Teckla' in b) + self.assertTrue('Steven Brust' in b) + self.assertEqual(b.title, 'Teckla') + self.assertEqual(b.author, 'Steven Brust') + self.assertRaises(AttributeError, getattr, b, 'genre') + self.assertRaises(TypeError, Book, title='Teckla', genre='fantasy') + self.assertRaises(TypeError, Book, author='Steven Brust') + + + +class TestStackoverflowAnswersV3(TestCase): + + def test_self_referential_directions(self): + # https://stackoverflow.com/a/64000706/208880 + class Directions(Enum): + # + NORTH = 1, 0 + WEST = 0, 1 + SOUTH = -1, 0 + EAST = 0, -1 + # + def __init__(self, x, y): + self.x = x + self.y = y + if len(self.__class__): + # make links + all = list(self.__class__) + left, right = all[0], all[-1] + self.left = left + self.right = right + left.right = self + right.left = self + # + D = Directions + self.assertEqual(D.NORTH.value, (1, 0)) + self.assertTrue(D.NORTH.left is D.WEST) + self.assertTrue(D.SOUTH.right is D.WEST) + + def test_self_referential_rock_paper_scissors(self): + # https://stackoverflow.com/a/57085357/208880 + class RPS(Enum): + # + Rock = "rock" + Paper = "paper" + Scissors = "scissors" + # + def __init__(self, value): + if len(self.__class__): + # make links + all = list(self.__class__) + first, previous = all[0], all[-1] + first.beats = self + self.beats = previous + # + self.assertTrue(RPS.Rock.beats is RPS.Scissors) + self.assertTrue(RPS.Scissors.beats is RPS.Paper) + self.assertTrue(RPS.Paper.beats is RPS.Rock) + + def test_arduino_headers(self): + # https://stackoverflow.com/q/65048495/208880 + class CHeader(Enum): + def __init_subclass__(cls, **kwds): + # write Enums to C header file + cls_name = cls.__name__ + header_path = getattr(cls, '_%s__header' % cls_name) + with open(header_path, 'w') as fh: + fh.write('initial header stuff here\n') + for enum in cls: + fh.write('#define %s %r\n' % (enum.name, enum.value)) + class Arduino(CHeader): + __header = os.path.join(tempdir, 'arduino.h') + ONE = 1 + TWO = 2 + with open(os.path.join(tempdir, 'arduino.h')) as fh: + data = fh.read() + self.assertEqual(textwrap.dedent("""\ + initial header stuff here + #define ONE 1 + #define TWO 2 + """), + data, + ) + + def test_create_C_like_Enum(self): + # https://stackoverflow.com/a/35965438/208880 + class Id(Enum, settings=MagicValue, start=0): + # + NONE # 0x0 + HEARTBEAT # 0x1 + FLUID_TRANSFER_REQUEST + FLUID_TRANSFER_STATUS_MSG + FLUID_TRANSFER_ERROR_MSG + # ... + # + # Camera App Messages + START_SENDING_PICTURES = 0x010000 + STOP_SENDING_PICTURES + START_RECORDING_VIDEO_REQ + STOP_RECORDING_VIDEO_REQ + # ... + # + # Sensor Calibration + VOLUME_REQUEST = 0x020000 + START_CAL + CLI_COMMAND_REQUEST + CLI_COMMAND_RESPONSE + # + # File Mananger + NEW_DELIVERY_REQ = 0x30000 + GET_DELIVERY_FILE_REQ + GET_FILE_REQ + # + ACK_NACK + RESPONSE + # + LAST_ID + # + self.assertEqual(Id.NONE.value, 0) + self.assertEqual(Id.FLUID_TRANSFER_ERROR_MSG.value, 4) + self.assertEqual(Id.START_SENDING_PICTURES.value, 0x010000) + self.assertEqual(Id.STOP_RECORDING_VIDEO_REQ.value, 0x010003) + self.assertEqual(Id.START_CAL.value, 0x020001) + self.assertEqual(Id.LAST_ID.value, 0x30005) + + + @unittest.skipUnless(pyparsing, 'pyparsing not installed') + def test_c_header_scanner(self): + # https://stackoverflow.com/questions/58732872/208880 + with open(os.path.join(tempdir, 'c_plus_plus.h'), 'w') as fh: + fh.write(""" + stuff before + enum hello { + Zero, + One, + Two, + Three, + Five=5, + Six, + Ten=10 + }; + in the middle + enum blah + { + alpha, + beta, + gamma = 10 , + zeta = 50 + }; + at the end + """) + from pyparsing import Group, Optional, Suppress, Word, ZeroOrMore + from pyparsing import alphas, alphanums, nums + # + CPPEnum = None + class CPPEnumType(EnumMeta): + # + @classmethod + def __prepare__(metacls, clsname, bases, **kwds): + # return a standard dictionary for the initial processing + return {} + # + def __init__(clsname, *args , **kwds): + super(CPPEnumType, clsname).__init__(*args) + # + def __new__(metacls, clsname, bases, clsdict, **kwds): + if CPPEnum is None: + # first time through, ignore the rest + enum_dict = super(CPPEnumType, metacls).__prepare__(clsname, bases, **kwds) + enum_dict.update(clsdict) + return super(CPPEnumType, metacls).__new__(metacls, clsname, bases, enum_dict, **kwds) + members = [] + # + # remove _file and _name using `pop()` as they will cause problems in EnumMeta + try: + file = clsdict.pop('_file') + except KeyError: + raise TypeError('_file not specified') + cpp_enum_name = clsdict.pop('_name', clsname.lower()) + with open(file) as fh: + file_contents = fh.read() + # + # syntax we don't want to see in the final parse tree + LBRACE, RBRACE, EQ, COMMA = map(Suppress, "{}=,") + _enum = Suppress("enum") + identifier = Word(alphas, alphanums + "_") + integer = Word(nums) + enumValue = Group(identifier("name") + Optional(EQ + integer("value"))) + enumList = Group(enumValue + ZeroOrMore(COMMA + enumValue)) + enum = _enum + identifier("enum") + LBRACE + enumList("names") + RBRACE + # + # find the cpp_enum_name ignoring other syntax and other enums + for item, start, stop in enum.scanString(file_contents): + if item.enum != cpp_enum_name: + continue + id = 0 + for entry in item.names: + if entry.value != "": + id = int(entry.value) + members.append((entry.name.upper(), id)) + id += 1 + # + # get the real EnumDict + enum_dict = super(CPPEnumType, metacls).__prepare__(clsname, bases, **kwds) + # transfer the original dict content, names starting with '_' first + items = list(clsdict.items()) + items.sort(key=lambda p: (0 if p[0][0] == '_' else 1, p)) + for name, value in items: + enum_dict[name] = value + # add the members + for name, value in members: + enum_dict[name] = value + return super(CPPEnumType, metacls).__new__(metacls, clsname, bases, enum_dict, **kwds) + # + class CPPEnum(IntEnum, metaclass=CPPEnumType): + pass + # + class Hello(CPPEnum): + _file = os.path.join(tempdir, 'c_plus_plus.h') + # + class Blah(CPPEnum): + _file = os.path.join(tempdir, 'c_plus_plus.h') + _name = 'blah' + # + self.assertEqual( + list(Hello), + [Hello.ZERO, Hello.ONE, Hello.TWO, Hello.THREE, Hello.FIVE, Hello.SIX, Hello.TEN], + ) + self.assertEqual(Hello.ZERO.value, 0) + self.assertEqual(Hello.THREE.value, 3) + self.assertEqual(Hello.SIX.value, 6) + self.assertEqual(Hello.TEN.value, 10) + # + self.assertEqual( + list(Blah), + [Blah.ALPHA, Blah.BETA, Blah.GAMMA, Blah.ZETA], + ) + self.assertEqual(Blah.ALPHA.value, 0) + self.assertEqual(Blah.BETA.value, 1) + self.assertEqual(Blah.GAMMA.value, 10) + self.assertEqual(Blah.ZETA.value, 50) + +class TestIssuesV3(TestCase): + """ + Problems that were stated in issues. + """ + + def test_auto_multi_int_1(self): + class Measurement(int, AddValueEnum, MultiValueEnum, start=0): + one = "20110721" + two = "20120911" + three = "20110518" + self.assertEqual([m.value for m in Measurement], [0, 1, 2]) + self.assertEqual([m.name for m in Measurement], ['one', 'two', 'three']) + self.assertIs(Measurement(0), Measurement.one) + self.assertIs(Measurement('20110721'), Measurement.one) + self.assertIs(Measurement(1), Measurement.two) + self.assertIs(Measurement('20120911'), Measurement.two) + self.assertIs(Measurement(2), Measurement.three) + self.assertIs(Measurement('20110518'), Measurement.three) + + def test_auto_multi_int_2(self): + class Measurement(int, Enum, settings=(MultiValue, AddValue), start=0): + one = "20110721" + two = "20120911" + three = "20110518" + self.assertEqual([m.value for m in Measurement], [0, 1, 2]) + self.assertEqual([m.name for m in Measurement], ['one', 'two', 'three']) + self.assertIs(Measurement(0), Measurement.one) + self.assertIs(Measurement('20110721'), Measurement.one) + self.assertIs(Measurement(1), Measurement.two) + self.assertIs(Measurement('20120911'), Measurement.two) + self.assertIs(Measurement(2), Measurement.three) + self.assertIs(Measurement('20110518'), Measurement.three) + + def test_extend_enum_with_init(self): + class Color(Enum, settings=MultiValue, init='foo bar'): + red = '1', 'yes' + green = '2', 'no' + blue = '3', 'maybe' + self.assertEqual(Color.red.value, '1') + self.assertEqual(Color.red.foo, '1') + self.assertEqual(Color.red.bar, 'yes') + extend_enum(Color, 'opacity', '4', 'never') + self.assertEqual(list(Color), [Color.red, Color.green, Color.blue, Color.opacity]) + self.assertEqual(Color.opacity.value, '4') + self.assertEqual(Color.opacity.name, 'opacity') + self.assertTrue(Color('4') is Color.opacity) + self.assertTrue(Color('never') is Color.opacity) + +class TestExtendEnumV3(TestCase): + + def test_extend_enum_plain(self): + class Color(Enum): + red = 1 + green = 2 + blue = 3 + self.assertRaisesRegex(TypeError, '.blue. already in use as property..Color.blue: 3.', extend_enum, Color, 'blue', 5) + # + extend_enum(Color, 'brown', 4) + self.assertEqual(Color.brown.name, 'brown') + self.assertEqual(Color.brown.value, 4) + self.assertTrue(Color.brown in Color) + self.assertEqual(Color(4), Color.brown) + self.assertEqual(Color['brown'], Color.brown) + self.assertEqual(len(Color), 4) + # + extend_enum(Color, 'mauve') + self.assertEqual(Color.mauve.name, 'mauve') + self.assertEqual(Color.mauve.value, 5) + self.assertTrue(Color.mauve in Color) + self.assertEqual(Color(5), Color.mauve) + self.assertEqual(Color['mauve'], Color.mauve) + self.assertEqual(len(Color), 5) + + def test_extend_enum_alias(self): + class Color(Enum): + red = 1 + green = 2 + blue = 3 + extend_enum(Color, 'rojo', 1) + self.assertEqual(Color.rojo.name, 'red') + self.assertEqual(Color.rojo.value, 1) + self.assertTrue(Color.rojo in Color) + self.assertEqual(Color(1), Color.rojo) + self.assertEqual(Color['rojo'], Color.red) + self.assertEqual(len(Color), 3) + + def test_extend_enum_unique(self): + class Color(UniqueEnum): + red = 1 + green = 2 + blue = 3 + self.assertRaisesRegex(ValueError, r' is a duplicate of ', extend_enum, Color, 'rojo', 1) + # + self.assertEqual(Color.red.name, 'red') + self.assertEqual(Color.red.value, 1) + self.assertTrue(Color.red in Color) + self.assertEqual(Color(1), Color.red) + self.assertEqual(Color['red'], Color.red) + self.assertEqual(Color.green.name, 'green') + self.assertEqual(Color.green.value, 2) + self.assertTrue(Color.green in Color) + self.assertEqual(Color(2), Color.green) + self.assertEqual(Color['blue'], Color.blue) + self.assertEqual(Color.blue.name, 'blue') + self.assertEqual(Color.blue.value, 3) + self.assertTrue(Color.blue in Color) + self.assertEqual(Color(3), Color.blue) + self.assertEqual(len(Color), 3) + # + extend_enum(Color, 'brown', 4) + self.assertEqual(Color.brown.name, 'brown') + self.assertEqual(Color.brown.value, 4) + self.assertTrue(Color.brown in Color) + self.assertEqual(Color(4), Color.brown) + self.assertEqual(Color['brown'], Color.brown) + self.assertEqual(len(Color), 4) + # + self.assertRaisesRegex(ValueError, ' is a duplicate of ', extend_enum, Color, 'verde', 2) + # + extend_enum(Color, 'mauve') + self.assertEqual(Color.mauve.name, 'mauve') + self.assertEqual(Color.mauve.value, 5) + self.assertTrue(Color.mauve in Color) + self.assertEqual(Color(5), Color.mauve) + self.assertEqual(Color['mauve'], Color.mauve) + self.assertEqual(len(Color), 5) + + + def test_extend_enum_shadow_property(self): + class Color(Enum): + red = 1 + green = 2 + blue = 3 + extend_enum(Color, 'value', 4) + self.assertEqual(Color.value.name, 'value') + self.assertEqual(Color.value.value, 4) + self.assertTrue(Color.value in Color) + self.assertEqual(Color(4), Color.value) + self.assertEqual(Color['value'], Color.value) + self.assertEqual(len(Color), 4) + self.assertEqual(Color.red.value, 1) + + def test_extend_enum_shadow_base(self): + class hohum(object): + def cyan(self): + "cyanize a color" + return self.value + class Color(hohum, Enum): + red = 1 + green = 2 + blue = 3 + self.assertRaisesRegex(TypeError, r'already in use in superclass', extend_enum, Color, 'cyan', 4) + self.assertEqual(len(Color), 3) + self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) + + def test_extend_enum_multivalue(self): + class Color(MultiValueEnum): + red = 1, 4, 7 + green = 2, 5, 8 + blue = 3, 6, 9 + extend_enum(Color, 'brown', 10, 20) + self.assertEqual(Color.brown.name, 'brown') + self.assertEqual(Color.brown.value, 10) + self.assertTrue(Color.brown in Color) + self.assertEqual(Color(10), Color.brown) + self.assertEqual(Color(20), Color.brown) + self.assertEqual(Color['brown'], Color.brown) + self.assertEqual(len(Color), 4) + # + self.assertRaisesRegex(ValueError, 'no values specified for MultiValue enum', extend_enum, Color, 'mauve') + + def test_extend_enum_multivalue_alias(self): + class Color(MultiValueEnum): + red = 1, 4, 7 + green = 2, 5, 8 + blue = 3, 6, 9 + self.assertRaisesRegex(ValueError, r' is a duplicate of ', extend_enum, Color, 'rojo', 7) + self.assertEqual(Color.red.name, 'red') + self.assertEqual(Color.red.value, 1) + self.assertTrue(Color.red in Color) + self.assertEqual(Color(1), Color.red) + self.assertEqual(Color(4), Color.red) + self.assertEqual(Color(7), Color.red) + self.assertEqual(Color['red'], Color.red) + self.assertEqual(Color.green.name, 'green') + self.assertEqual(Color.green.value, 2) + self.assertTrue(Color.green in Color) + self.assertEqual(Color(2), Color.green) + self.assertEqual(Color(5), Color.green) + self.assertEqual(Color(8), Color.green) + self.assertEqual(Color['blue'], Color.blue) + self.assertEqual(Color.blue.name, 'blue') + self.assertEqual(Color.blue.value, 3) + self.assertTrue(Color.blue in Color) + self.assertEqual(Color(3), Color.blue) + self.assertEqual(Color(6), Color.blue) + self.assertEqual(Color(9), Color.blue) + self.assertEqual(len(Color), 3) + + def test_extend_enum_multivalue_str(self): + class M(str, MultiValueEnum): + VALUE_1 = 'value_1', 'VALUE_1' + VALUE_2 = 'value_2', 'VALUE_2' + VALUE_3 = 'value_3', 'VALUE_3' + self.assertTrue(M._member_type_ is str) + extend_enum(M, 'VALUE_4', 'value_4', 'VALUE_4') + self.assertEqual(list(M), [M.VALUE_1, M.VALUE_2, M.VALUE_3, M.VALUE_4]) + self.assertTrue(M('value_4') is M.VALUE_4) + self.assertTrue(M('VALUE_4') is M.VALUE_4) + self.assertTrue(M.VALUE_4.name == 'VALUE_4') + self.assertTrue(M.VALUE_4.value == 'value_4') + + def test_extend_intenum(self): + class Index(IntEnum): + DeviceType = 0x1000 + ErrorRegister = 0x1001 + + for name, value in ( + ('ControlWord', 0x6040), + ('StatusWord', 0x6041), + ('OperationMode', 0x6060), + ): + extend_enum(Index, name, value) + + self.assertEqual(len(Index), 5) + self.assertEqual(list(Index), [Index.DeviceType, Index.ErrorRegister, Index.ControlWord, Index.StatusWord, Index.OperationMode]) + self.assertEqual(Index.DeviceType.value, 0x1000) + self.assertEqual(Index.StatusWord.value, 0x6041) + + def test_extend_multi_init(self): + class HTTPStatus(IntEnum): + def __new__(cls, value, phrase, description): + obj = int.__new__(cls, value) + obj._value_ = value + + obj.phrase = phrase + obj.description = description + return obj + CONTINUE = 100, 'Continue', 'Request received, please continue' + SWITCHING_PROTOCOLS = 101, 'Switching Protocols', 'Switching to new protocol; obey Upgrade header' + PROCESSING = 102, 'Processing', '' + length = 3 + extend_enum(HTTPStatus, 'BAD_SPAM', 513, 'Too greasy', 'for a train') + extend_enum(HTTPStatus, 'BAD_EGGS', 514, 'Too green', '') + self.assertEqual(len(HTTPStatus), length+2) + self.assertEqual( + list(HTTPStatus)[-2:], + [HTTPStatus.BAD_SPAM, HTTPStatus.BAD_EGGS], + ) + self.assertEqual(HTTPStatus.BAD_SPAM.value, 513) + self.assertEqual(HTTPStatus.BAD_SPAM.name, 'BAD_SPAM') + self.assertEqual(HTTPStatus.BAD_SPAM.phrase, 'Too greasy') + self.assertEqual(HTTPStatus.BAD_SPAM.description, 'for a train') + self.assertEqual(HTTPStatus.BAD_EGGS.value, 514) + self.assertEqual(HTTPStatus.BAD_EGGS.name, 'BAD_EGGS') + self.assertEqual(HTTPStatus.BAD_EGGS.phrase, 'Too green') + self.assertEqual(HTTPStatus.BAD_EGGS.description, '') + + def test_extend_flag(self): + class Color(Flag): + BLACK = 0 + RED = 1 + GREEN = 2 + BLUE = 4 + extend_enum(Color, 'MAGENTA') + self.assertTrue(Color(8) is Color.MAGENTA) + self.assertTrue(isinstance(Color.MAGENTA, Color)) + self.assertEqual(Color.MAGENTA.value, 8) + extend_enum(Color, 'PURPLE', 11) + self.assertTrue(Color(11) is Color.PURPLE) + self.assertTrue(isinstance(Color.PURPLE, Color)) + self.assertEqual(Color.PURPLE.value, 11) + self.assertTrue(issubclass(Color, Flag)) + + def test_extend_flag_backwards(self): + class Color(Flag): + BLACK = 0 + RED = 1 + GREEN = 2 + BLUE = 4 + extend_enum(Color, 'PURPLE', 11) + self.assertTrue(Color(11) is Color.PURPLE) + self.assertTrue(isinstance(Color.PURPLE, Color)) + self.assertEqual(Color.PURPLE.value, 11) + self.assertTrue(issubclass(Color, Flag)) + # + extend_enum(Color, 'MAGENTA') + self.assertTrue(Color(8) is Color.MAGENTA) + self.assertTrue(isinstance(Color.MAGENTA, Color)) + self.assertEqual(Color.MAGENTA.value, 8) + # + extend_enum(Color, 'mauve') + self.assertEqual(Color.mauve.name, 'mauve') + self.assertEqual(Color.mauve.value, 16) + self.assertTrue(Color.mauve in Color) + self.assertEqual(Color(16), Color.mauve) + self.assertEqual(Color['mauve'], Color.mauve) + self.assertEqual(len(Color), 5) + + def test_extend_intflag(self): + class Color(IntFlag): + BLACK = 0 + RED = 1 + GREEN = 2 + BLUE = 4 + extend_enum(Color, 'MAGENTA') + self.assertTrue(Color(8) is Color.MAGENTA) + self.assertTrue(isinstance(Color.MAGENTA, Color)) + self.assertEqual(Color.MAGENTA.value, 8) + extend_enum(Color, 'PURPLE', 11) + self.assertTrue(Color(11) is Color.PURPLE) + self.assertTrue(isinstance(Color.PURPLE, Color)) + self.assertEqual(Color.PURPLE.value, 11) + self.assertTrue(issubclass(Color, Flag)) + # + extend_enum(Color, 'mauve') + self.assertEqual(Color.mauve.name, 'mauve') + self.assertEqual(Color.mauve.value, 16) + self.assertTrue(Color.mauve in Color) + self.assertEqual(Color(16), Color.mauve) + self.assertEqual(Color['mauve'], Color.mauve) + self.assertEqual(len(Color), 5) + + def test_extend_intflag_backwards(self): + class Color(IntFlag): + BLACK = 0 + RED = 1 + GREEN = 2 + BLUE = 4 + extend_enum(Color, 'PURPLE', 11) + self.assertTrue(Color(11) is Color.PURPLE) + self.assertTrue(isinstance(Color.PURPLE, Color)) + self.assertEqual(Color.PURPLE.value, 11) + self.assertTrue(issubclass(Color, Flag)) + # + extend_enum(Color, 'MAGENTA') + self.assertTrue(Color(8) is Color.MAGENTA) + self.assertTrue(isinstance(Color.MAGENTA, Color)) + self.assertEqual(Color.MAGENTA.value, 8) + # + extend_enum(Color, 'mauve') + self.assertEqual(Color.mauve.name, 'mauve') + self.assertEqual(Color.mauve.value, 16) + self.assertTrue(Color.mauve in Color) + self.assertEqual(Color(16), Color.mauve) + self.assertEqual(Color['mauve'], Color.mauve) + self.assertEqual(len(Color), 5) + + def test_extend_strenum(self): + class Color(StrEnum): + RED = auto() + GREEN = auto() + BLUE = auto() + extend_enum(Color, 'BLACK') + self.assertEqual(Color.BLACK.name, 'BLACK') + self.assertEqual(Color.BLACK.value, 'black') + self.assertEqual(len(Color), 4) + + @unittest.skipUnless(StdlibEnum, 'Stdlib Enum not available') + def test_extend_enum_stdlib(self): + class Color(StdlibEnum): + red = 1 + green = 2 + blue = 3 + self.assertEqual(getattr(Color.red, '_values_', None), None) + extend_enum(Color, 'brown', 4) + self.assertEqual(Color.brown.name, 'brown') + self.assertEqual(Color.brown.value, 4) + self.assertTrue(Color.brown in Color) + self.assertEqual(Color(4), Color.brown) + self.assertEqual(Color['brown'], Color.brown) + self.assertEqual(len(Color), 4) + + @unittest.skipUnless(StdlibEnum, 'Stdlib Enum not available') + def test_extend_enum_plain_stdlib(self): + class Color(StdlibEnum): + red = 1 + green = 2 + blue = 3 + self.assertRaisesRegex(TypeError, 'already in use as', extend_enum, Color, 'blue', 5) + # + extend_enum(Color, 'brown', 4) + self.assertEqual(Color.brown.name, 'brown') + self.assertEqual(Color.brown.value, 4) + self.assertTrue(Color.brown in Color) + self.assertEqual(Color(4), Color.brown) + self.assertEqual(Color['brown'], Color.brown) + self.assertEqual(len(Color), 4) + self.assertEqual(list(Color), [Color.red, Color.green, Color.blue, Color.brown]) + self.assertEqual([c.value for c in Color], [1, 2, 3, 4]) + # + extend_enum(Color, 'mauve') + self.assertEqual(Color.mauve.name, 'mauve') + self.assertEqual(Color.mauve.value, 5) + self.assertTrue(Color.mauve in Color) + self.assertEqual(Color(5), Color.mauve) + self.assertEqual(Color['mauve'], Color.mauve) + self.assertEqual(len(Color), 5) + + @unittest.skipUnless(StdlibEnum, 'Stdlib Enum not available') + def test_extend_enum_alias_stdlib(self): + class Color(StdlibEnum): + red = 1 + green = 2 + blue = 3 + extend_enum(Color, 'rojo', 1) + self.assertEqual(Color.rojo.name, 'red') + self.assertEqual(Color.rojo.value, 1) + self.assertTrue(Color.rojo in Color) + self.assertEqual(Color(1), Color.rojo) + self.assertEqual(Color['rojo'], Color.red) + self.assertEqual(len(Color), 3) + + @unittest.skipUnless(StdlibEnum, 'Stdlib Enum not available') + def test_extend_enum_shadow_property_stdlib(self): + class Color(StdlibEnum): + red = 1 + green = 2 + blue = 3 + extend_enum(Color, 'value', 4) + self.assertEqual(Color.value.name, 'value') + self.assertEqual(Color.value.value, 4) + self.assertTrue(Color.value in Color) + self.assertEqual(Color(4), Color.value) + self.assertEqual(Color['value'], Color.value) + self.assertEqual(len(Color), 4) + self.assertEqual(Color.red.value, 1) + + @unittest.skipUnless(StdlibEnum, 'Stdlib Enum not available') + def test_extend_enum_shadow_base_stdlib(self): + class hohum(object): + def cyan(self): + "cyanize a color" + return self.value + class Color(hohum, StdlibEnum): + red = 1 + green = 2 + blue = 3 + self.assertRaisesRegex(TypeError, r'already in use in superclass', extend_enum, Color, 'cyan', 4) + self.assertEqual(len(Color), 3) + self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) + + @unittest.skipUnless(StdlibIntEnum, 'Stdlib IntEnum not available') + def test_extend_intenum_stdlib(self): + class Index(StdlibIntEnum): + DeviceType = 0x1000 + ErrorRegister = 0x1001 + + for name, value in ( + ('ControlWord', 0x6040), + ('StatusWord', 0x6041), + ('OperationMode', 0x6060), + ): + extend_enum(Index, name, value) + + self.assertEqual(len(Index), 5) + self.assertEqual(list(Index), [Index.DeviceType, Index.ErrorRegister, Index.ControlWord, Index.StatusWord, Index.OperationMode]) + self.assertEqual(Index.DeviceType.value, 0x1000) + self.assertEqual(Index.StatusWord.value, 0x6041) + + @unittest.skipUnless(StdlibIntEnum, 'Stdlib IntEnum not available') + def test_extend_multi_init_stdlib(self): + class HTTPStatus(StdlibIntEnum): + def __new__(cls, value, phrase, description): + obj = int.__new__(cls, value) + obj._value_ = value + + obj.phrase = phrase + obj.description = description + return obj + CONTINUE = 100, 'Continue', 'Request received, please continue' + SWITCHING_PROTOCOLS = 101, 'Switching Protocols', 'Switching to new protocol; obey Upgrade header' + PROCESSING = 102, 'Processing', '' + length = 3 + extend_enum(HTTPStatus, 'BAD_SPAM', 513, 'Too greasy', 'for a train') + extend_enum(HTTPStatus, 'BAD_EGGS', 514, 'Too green', '') + self.assertEqual(len(HTTPStatus), length+2) + self.assertEqual( + list(HTTPStatus)[-2:], + [HTTPStatus.BAD_SPAM, HTTPStatus.BAD_EGGS], + ) + self.assertEqual(HTTPStatus.BAD_SPAM.value, 513) + self.assertEqual(HTTPStatus.BAD_SPAM.name, 'BAD_SPAM') + self.assertEqual(HTTPStatus.BAD_SPAM.phrase, 'Too greasy') + self.assertEqual(HTTPStatus.BAD_SPAM.description, 'for a train') + self.assertEqual(HTTPStatus.BAD_EGGS.value, 514) + self.assertEqual(HTTPStatus.BAD_EGGS.name, 'BAD_EGGS') + self.assertEqual(HTTPStatus.BAD_EGGS.phrase, 'Too green') + self.assertEqual(HTTPStatus.BAD_EGGS.description, '') + + @unittest.skipUnless(StdlibFlag, 'Stdlib Flag not available') + def test_extend_flag_stdlib(self): + class Color(StdlibFlag): + BLACK = 0 + RED = 1 + GREEN = 2 + BLUE = 4 + extend_enum(Color, 'MAGENTA') + self.assertTrue(Color(8) is Color.MAGENTA) + self.assertTrue(isinstance(Color.MAGENTA, Color)) + self.assertEqual(Color.MAGENTA.value, 8) + extend_enum(Color, 'PURPLE', 11) + self.assertTrue(Color(11) is Color.PURPLE) + self.assertTrue(isinstance(Color.PURPLE, Color)) + self.assertEqual(Color.PURPLE.value, 11) + self.assertTrue(issubclass(Color, StdlibFlag)) + + @unittest.skipUnless(StdlibFlag, 'Stdlib Flag not available') + def test_extend_flag_backwards_stdlib(self): + class Color(StdlibFlag): + BLACK = 0 + RED = 1 + GREEN = 2 + BLUE = 4 + extend_enum(Color, 'PURPLE', 11) + self.assertTrue(Color(11) is Color.PURPLE) + self.assertTrue(isinstance(Color.PURPLE, Color)) + self.assertEqual(Color.PURPLE.value, 11) + self.assertTrue(issubclass(Color, StdlibFlag)) + # + extend_enum(Color, 'MAGENTA') + self.assertTrue(Color(16) is Color.MAGENTA) + self.assertTrue(isinstance(Color.MAGENTA, Color)) + self.assertEqual(Color.MAGENTA.value,16) + # + extend_enum(Color, 'mauve') + self.assertEqual(Color.mauve.name, 'mauve') + self.assertEqual(Color.mauve.value, 32) + self.assertTrue(Color.mauve in Color) + self.assertEqual(Color(32), Color.mauve) + self.assertEqual(Color['mauve'], Color.mauve) + + @unittest.skipUnless(StdlibIntFlag, 'Stdlib IntFlag not available') + def test_extend_intflag_stdlib(self): + class Color(StdlibIntFlag): + BLACK = 0 + RED = 1 + GREEN = 2 + BLUE = 4 + extend_enum(Color, 'MAGENTA') + self.assertTrue(Color(8) is Color.MAGENTA) + self.assertTrue(isinstance(Color.MAGENTA, Color)) + self.assertEqual(Color.MAGENTA.value, 8) + extend_enum(Color, 'PURPLE', 11) + self.assertTrue(Color(11) is Color.PURPLE) + self.assertTrue(isinstance(Color.PURPLE, Color)) + self.assertEqual(Color.PURPLE.value, 11) + self.assertTrue(issubclass(Color, StdlibFlag)) + # + extend_enum(Color, 'mauve') + self.assertEqual(Color.mauve.name, 'mauve') + self.assertEqual(Color.mauve.value, 16) + self.assertTrue(Color.mauve in Color) + self.assertEqual(Color(16), Color.mauve) + self.assertEqual(Color['mauve'], Color.mauve) + + @unittest.skipUnless(StdlibIntFlag, 'Stdlib IntFlag not available') + def test_extend_intflag_backwards_stdlib(self): + class Color(StdlibIntFlag): + BLACK = 0 + RED = 1 + GREEN = 2 + BLUE = 4 + if pyver >= PY3_11: + # flags make more sense in 3.11 + length = 5 + MAGENTA = 8 + mauve = 16 + else: + length = 7 + MAGENTA = 16 + mauve = 32 + extend_enum(Color, 'PURPLE', 11) + self.assertTrue(Color(11) is Color.PURPLE) + self.assertTrue(isinstance(Color.PURPLE, Color)) + self.assertEqual(Color.PURPLE.value, 11) + self.assertTrue(issubclass(Color, StdlibFlag)) + # + extend_enum(Color, 'MAGENTA') + self.assertTrue(Color(MAGENTA) is Color.MAGENTA) + self.assertTrue(isinstance(Color.MAGENTA, Color)) + self.assertEqual(Color.MAGENTA.value, MAGENTA) + # + extend_enum(Color, 'mauve') + self.assertEqual(Color.mauve.name, 'mauve') + self.assertEqual(Color.mauve.value, mauve) + self.assertTrue(Color.mauve in Color) + self.assertEqual(Color(mauve), Color.mauve) + self.assertEqual(Color['mauve'], Color.mauve) + self.assertEqual(len(Color), length, list(Color)) + + @unittest.skipUnless(StdlibStrEnum, 'Stdlib StrEnum not available') + def test_extend_strenum_stdlib(self): + class Color(StrEnum): + RED = auto() + GREEN = auto() + BLUE = auto() + extend_enum(Color, 'BLACK') + self.assertEqual(Color.BLACK.name, 'BLACK') + self.assertEqual(Color.BLACK.value, 'black') + self.assertEqual(len(Color), 4) + + +if __name__ == '__main__': + raise RuntimeError("'test_v3.py' should not be run by itself; it's included in 'test.py'") diff --git a/venv/Lib/site-packages/blendmodes/__init__.py b/venv/Lib/site-packages/blendmodes/__init__.py new file mode 100644 index 00000000..2b6e91ad --- /dev/null +++ b/venv/Lib/site-packages/blendmodes/__init__.py @@ -0,0 +1,2 @@ +"""Use this module to apply a number of blending modes to a background and foreground image +""" diff --git a/venv/Lib/site-packages/blendmodes/__pycache__/__init__.cpython-39.pyc b/venv/Lib/site-packages/blendmodes/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..da1d6a38859540c7045ecb683d0533d09663f8a9 GIT binary patch literal 285 zcmYjMK~BUl3{3aJYNh>yuX`vDKnU@Iy#NPLP~0LCRtQI!8`a1FW4(jKwLQC z77kb%+ap*%tC#z`M34`hUav|*$_$^&+^+3%{;nK3Rj#=NOU|ekNO3AITqe&sHbSR7cU3|4A>V7DL(? U_MDA5kJD*=`J3cwj{GT5KLp5FlmGw# literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/blendmodes/__pycache__/blend.cpython-39.pyc b/venv/Lib/site-packages/blendmodes/__pycache__/blend.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..231846003cc72829e6134e11092b415beb764453 GIT binary patch literal 13302 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 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/blendmodes/__pycache__/blendtype.cpython-39.pyc b/venv/Lib/site-packages/blendmodes/__pycache__/blendtype.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c599cfb826eeeee7b7784bf75fcdf6e55c3f9253 GIT binary patch literal 2850 zcmbVONpIUm6sA^^W7*mFG*NsgV4%*S$Dj?ONVFqVmI9h`(}7Td){Jc;6sdA?;#}K9 zZ~Yzp1^pBK1@GEZe?Tui_04dGWG_8bhB@C`W_fSkGXC~9G4S`-@4xuJ*@p3lAnLyk z60Z!Sk$#DQ8Ps46)(ji#CT%tZZiTINo315Odq5wthx9RfL_cGX z=@a%DeafEDK6^@^u|8V+4D8p!HV;knKeY|xzilVPp3dB(!KD9nfA@6#B8ro>AMSq9 z-&Kg%NArm{emnV2X{?1;HsO`fDKW7JqtVzKpEyd(XDrBqOD?oZOzh!oGMl@v=k8SL zt>Q4wlhZtjgl3J>x}(|XP%m1DS zVf;Y|gR zM*LhxmDsSv#GZ@~-*}Ez3WM{Dj7v9Yh?@@FZ%Pg3C+`adg@}DTbe&S3t`c6z6(aV# znX3%`7$;(TG2pb>fhTuY!Zbe10h&{4)+k~-?}n54aTQVg<0{NkEL7r*6@i{|j=ixCDaO+*kSYol^4VNx ziD1XM%n}XJYwmF1&ED$JNLGFpZ)9L7Sgcah5p%UtiHW_}{A8CU1-eB+LFN`Rb>#p{ zBPGzq0xXSCu#Gc{n-G-(NQF_jQwn8efUQC*2^13)s}caF0?0OzEs`JF;;Zyjwu7kv zvc=X_ZUd<>3WX&)p~2EfEhcZ0M_?*|Y~hX~ccEL1idiHqbc+$0UDU)}g%*C0>>*j^ zCD66WVQ~dgl~U*yCj?TJM6h(p15%YWsLB?uCJPgoV#ERjrYbXF z363ZxzZ%I9UsCe(_xIk5#VTfevAN1F;%MRLS-i%|z1qi8gO9g;k%zJZ?tkE?d9b+T z(d8lxP8V2_`ToXVz4y;~x{&WYyc-S0i|_g7D#3@5Vc~!JQh-K#x83-^e{!wqo|$}( zONmf8%dyK~C?ri}(2mdX^_aATu$5+sRIe;8&`3zFuZkdgDPERo7eDQ0ui0z;18nYT A{Qv*} literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/blendmodes/blend.py b/venv/Lib/site-packages/blendmodes/blend.py new file mode 100644 index 00000000..4165bfa7 --- /dev/null +++ b/venv/Lib/site-packages/blendmodes/blend.py @@ -0,0 +1,511 @@ +"""Provide blending functions and types. + +Adapted from https://github.com/addisonElliott/pypdn/blob/master/pypdn/reader.py +and https://gitlab.com/inklabapp/pyora/-/blob/master/pyora/BlendNonSep.py +MIT License Copyright (c) 2020 FredHappyface + +Credits to: + +MIT License Copyright (c) 2019 Paul Jewell +For implementing blending from the Open Raster Image Spec + +MIT License Copyright (c) 2018 Addison Elliott +For implementing blending from Paint.NET + +MIT License Copyright (c) 2017 pashango +For implementing a number of blending functions used by other popular image +editors +""" + +from __future__ import annotations + +import warnings + +import numpy as np +from PIL import Image + +from .blendtype import BlendType + + +def normal(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.NORMAL.""" + del background # we don't care about this + return foreground + + +def multiply(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.MULTIPLY.""" + return np.clip(foreground * background, 0.0, 1.0) + + +def additive(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.ADDITIVE.""" + return np.minimum(background + foreground, 1.0) + + +def colourburn(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.COLOURBURN.""" + with np.errstate(divide="ignore"): + return np.where( + foreground != 0.0, np.maximum(1.0 - ((1.0 - background) / foreground), 0.0), 0.0 + ) + + +def colourdodge(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.COLOURDODGE.""" + with np.errstate(divide="ignore"): + return np.where(foreground != 1.0, np.minimum(background / (1.0 - foreground), 1.0), 1.0) + + +def reflect(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.REFLECT.""" + with np.errstate(divide="ignore"): + return np.where( + foreground != 1.0, np.minimum((background ** 2) / (1.0 - foreground), 1.0), 1.0 + ) + + +def glow(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.GLOW.""" + with np.errstate(divide="ignore"): + return np.where( + background != 1.0, np.minimum((foreground ** 2) / (1.0 - background), 1.0), 1.0 + ) + + +def overlay(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.OVERLAY.""" + return np.where( + background < 0.5, + 2 * background * foreground, + 1.0 - (2 * (1.0 - background) * (1.0 - foreground)), + ) + + +def difference(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.DIFFERENCE.""" + return np.abs(background - foreground) + + +def negation(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.NEGATION.""" + return np.maximum(background - foreground, 0.0) + + +def lighten(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.LIGHTEN.""" + return np.maximum(background, foreground) + + +def darken(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.DARKEN.""" + return np.minimum(background, foreground) + + +def screen(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.SCREEN.""" + return background + foreground - background * foreground + + +def xor(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.XOR.""" + # XOR requires int values so convert to uint8 + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + return imageIntToFloat(imageFloatToInt(background) ^ imageFloatToInt(foreground)) + + +def softlight(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.SOFTLIGHT.""" + return (1.0 - background) * background * foreground + background * ( + 1.0 - (1.0 - background) * (1.0 - foreground) + ) + + +def hardlight(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.HARDLIGHT.""" + return np.where( + foreground < 0.5, + np.minimum(background * 2 * foreground, 1.0), + np.minimum(1.0 - ((1.0 - background) * (1.0 - (foreground - 0.5) * 2.0)), 1.0), + ) + + +def grainextract(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.GRAINEXTRACT.""" + return np.clip(background - foreground + 0.5, 0.0, 1.0) + + +def grainmerge(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.GRAINMERGE.""" + return np.clip(background + foreground - 0.5, 0.0, 1.0) + + +def divide(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.DIVIDE.""" + return np.minimum((256.0 / 255.0 * background) / (1.0 / 255.0 + foreground), 1.0) + + +def pinlight(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.PINLIGHT.""" + return np.minimum(background, 2 * foreground) * (foreground < 0.5) + np.maximum( + background, 2 * (foreground - 0.5) + ) * (foreground >= 0.5) + + +def vividlight(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.VIVIDLIGHT.""" + return colourburn(background, foreground * 2) * (foreground < 0.5) + colourdodge( + background, 2 * (foreground - 0.5) + ) * (foreground >= 0.5) + + +def exclusion(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.EXCLUSION.""" + return background + foreground - (2.0 * background * foreground) + + +def _lum(colours: np.ndarray) -> np.ndarray: + """Luminosity. + + :param colours: x by x by 3 matrix of rgb color components of pixels + :return: x by x by 3 matrix of luminosity of pixels + """ + return (colours[:, :, 0] * 0.299) + (colours[:, :, 1] * 0.587) + (colours[:, :, 2] * 0.114) + + +def _setLum(originalColours: np.ndarray, newLuminosity: np.ndarray) -> np.ndarray: + """Set a new luminosity value for the matrix of color.""" + _colours = originalColours.copy() + _luminosity = _lum(_colours) + deltaLum = newLuminosity - _luminosity + _colours[:, :, 0] += deltaLum + _colours[:, :, 1] += deltaLum + _colours[:, :, 2] += deltaLum + _luminosity = _lum(_colours) + _minColours = np.min(_colours, axis=2) + _MaxColours = np.max(_colours, axis=2) + for i in range(_colours.shape[0]): + for j in range(_colours.shape[1]): + _colour = _colours[i][j] + newLuminosity = _luminosity[i, j] + minColour = _minColours[i, j] + maxColour = _MaxColours[i, j] + if minColour < 0: + _colours[i][j] = newLuminosity + ( + ((_colour - newLuminosity) * newLuminosity) / (newLuminosity - minColour) + ) + if maxColour > 1: + _colours[i][j] = newLuminosity + ( + ((_colour - newLuminosity) * (1 - newLuminosity)) / (maxColour - newLuminosity) + ) + return _colours + + +def _sat(colours: np.ndarray) -> np.ndarray: + """Saturation. + + :param colours: x by x by 3 matrix of rgb color components of pixels + :return: int of saturation of pixels + """ + return np.max(colours, axis=2) - np.min(colours, axis=2) + + +def _setSat(originalColours: np.ndarray, newSaturation: np.ndarray) -> np.ndarray: + """Set a new saturation value for the matrix of color. + + The current implementation cannot be vectorized in an efficient manner, + so it is very slow, + O(m*n) at least. This might be able to be improved with openCL if that is + the direction that the lib takes. + :param c: x by x by 3 matrix of rgb color components of pixels + :param s: int of the new saturation value for the matrix + :return: x by x by 3 matrix of luminosity of pixels + """ + _colours = originalColours.copy() + for i in range(_colours.shape[0]): + for j in range(_colours.shape[1]): + _colour = _colours[i][j] + minI = 0 + midI = 1 + maxI = 2 + if _colour[midI] < _colour[minI]: + minI, midI = midI, minI + if _colour[maxI] < _colour[midI]: + midI, maxI = maxI, midI + if _colour[midI] < _colour[minI]: + minI, midI = midI, minI + if _colour[maxI] - _colour[minI] > 0.0: + _colours[i][j][midI] = ((_colour[midI] - _colour[minI]) * newSaturation[i, j]) / ( + _colour[maxI] - _colour[minI] + ) + _colours[i][j][maxI] = newSaturation[i, j] + else: + _colours[i][j][midI] = 0 + _colours[i][j][maxI] = 0 + _colours[i][j][minI] = 0 + return _colours + + +def hue(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.HUE.""" + return _setLum(_setSat(foreground, _sat(background)), _lum(background)) + + +def saturation(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.SATURATION.""" + return _setLum(_setSat(background, _sat(foreground)), _lum(background)) + + +def colour(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.COLOUR.""" + return _setLum(foreground, _lum(background)) + + +def luminosity(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: + """BlendType.LUMINOSITY.""" + return _setLum(background, _lum(foreground)) + + +def destin( + backgroundAlpha: np.ndarray, + foregroundAlpha: np.ndarray, + backgroundColour: np.ndarray, + foregroundColour: np.ndarray, +): + """'clip' composite mode. + + All parts of 'layer above' which are alpha in 'layer below' will be made + also alpha in 'layer above' + (to whatever degree of alpha they were) + + Destination which overlaps the source, replaces the source. + + Fa = 0; Fb = αs + co = αb x Cb x αs + αo = αb x αs + """ + del foregroundColour # Not used by function + outAlpha = backgroundAlpha * foregroundAlpha + with np.errstate(divide="ignore", invalid="ignore"): + outRGB = np.divide( + np.multiply((backgroundAlpha * foregroundAlpha)[:, :, None], backgroundColour), + outAlpha[:, :, None], + ) + return outRGB, outAlpha + + +def destout( + backgroundAlpha: np.ndarray, + foregroundAlpha: np.ndarray, + backgroundColour: np.ndarray, + foregroundColour: np.ndarray, +): + """Reverse 'Clip' composite mode. + + All parts of 'layer below' which are alpha in 'layer above' will be made + also alpha in 'layer below' + (to whatever degree of alpha they were) + + """ + del foregroundColour # Not used by function + outAlpha = backgroundAlpha * (1 - foregroundAlpha) + with np.errstate(divide="ignore", invalid="ignore"): + outRGB = np.divide( + np.multiply((backgroundAlpha * (1 - foregroundAlpha))[:, :, None], backgroundColour), + outAlpha[:, :, None], + ) + return outRGB, outAlpha + + +def destatop( + backgroundAlpha: np.ndarray, + foregroundAlpha: np.ndarray, + backgroundColour: np.ndarray, + foregroundColour: np.ndarray, +): + """Place the layer below above the 'layer above' in places where the 'layer above' exists... + + where 'layer below' does not exist, but 'layer above' does, place 'layer-above' + + """ + outAlpha = (foregroundAlpha * (1 - backgroundAlpha)) + (backgroundAlpha * foregroundAlpha) + with np.errstate(divide="ignore", invalid="ignore"): + outRGB = np.divide( + np.multiply((foregroundAlpha * (1 - backgroundAlpha))[:, :, None], foregroundColour) + + np.multiply((backgroundAlpha * foregroundAlpha)[:, :, None], backgroundColour), + outAlpha[:, :, None], + ) + return outRGB, outAlpha + + +def srcatop( + backgroundAlpha: np.ndarray, + foregroundAlpha: np.ndarray, + backgroundColour: np.ndarray, + foregroundColour: np.ndarray, +): + """Place the layer below above the 'layer above' in places where the 'layer above' exists.""" + outAlpha = (foregroundAlpha * backgroundAlpha) + (backgroundAlpha * (1 - foregroundAlpha)) + with np.errstate(divide="ignore", invalid="ignore"): + outRGB = np.divide( + np.multiply((foregroundAlpha * backgroundAlpha)[:, :, None], foregroundColour) + + np.multiply((backgroundAlpha * (1 - foregroundAlpha))[:, :, None], backgroundColour), + outAlpha[:, :, None], + ) + + return outRGB, outAlpha + + +def imageIntToFloat(image: np.ndarray) -> np.ndarray: + """Convert a numpy array representing an image to an array of floats. + + Args: + image (np.ndarray): numpy array of ints + + Returns: + np.ndarray: numpy array of floats + """ + return image / 255 + + +def imageFloatToInt(image: np.ndarray) -> np.ndarray: + """Convert a numpy array representing an image to an array of ints. + + Args: + image (np.ndarray): numpy array of floats + + Returns: + np.ndarray: numpy array of ints + """ + return (image * 255).astype(np.uint8) + + +def blend(background: np.ndarray, foreground: np.ndarray, blendType: BlendType) -> np.ndarray: + """Blend pixels. + + Args: + background (np.ndarray): background + foreground (np.ndarray): foreground + blendType (BlendType): the blend type + + Returns: + np.ndarray: new array representing the image + + background: np.ndarray, + foreground: np.ndarray and the return are in the form + + [[[0. 0. 0.] + [0. 0. 0.] + [0. 0. 0.] + ... + [0. 0. 0.] + [0. 0. 0.] + [0. 0. 0.]] + + ... + + [[0. 0. 0.] + [0. 0. 0.] + [0. 0. 0.] + ... + [0. 0. 0.] + [0. 0. 0.] + [0. 0. 0.]]] + """ + blendLookup = { + BlendType.NORMAL: normal, + BlendType.MULTIPLY: multiply, + BlendType.COLOURBURN: colourburn, + BlendType.COLOURDODGE: colourdodge, + BlendType.REFLECT: reflect, + BlendType.OVERLAY: overlay, + BlendType.DIFFERENCE: difference, + BlendType.LIGHTEN: lighten, + BlendType.DARKEN: darken, + BlendType.SCREEN: screen, + BlendType.SOFTLIGHT: softlight, + BlendType.HARDLIGHT: hardlight, + BlendType.GRAINEXTRACT: grainextract, + BlendType.GRAINMERGE: grainmerge, + BlendType.DIVIDE: divide, + BlendType.HUE: hue, + BlendType.SATURATION: saturation, + BlendType.COLOUR: colour, + BlendType.LUMINOSITY: luminosity, + BlendType.XOR: xor, + BlendType.NEGATION: negation, + BlendType.PINLIGHT: pinlight, + BlendType.VIVIDLIGHT: vividlight, + BlendType.EXCLUSION: exclusion, + } + + if blendType not in blendLookup: + return normal(background, foreground) + return blendLookup[blendType](background, foreground) + + +def blendLayers( + background: Image.Image, + foreground: Image.Image, + blendType: BlendType | tuple[str, ...], + opacity: float = 1.0, +) -> Image.Image: + """Blend layers using numpy array. + + Args: + background (Image.Image): background layer + foreground (Image.Image): foreground layer (must be same size as background) + blendType (BlendType): The blendtype + opacity (float): The opacity of the foreground image + + Returns: + Image.Image: combined image + """ + # Convert the Image.Image to a numpy array + npForeground: np.ndarray = imageIntToFloat(np.array(foreground.convert("RGBA"))) + npBackground: np.ndarray = imageIntToFloat(np.array(background.convert("RGBA"))) + + # Get the alpha from the layers + backgroundAlpha = npBackground[:, :, 3] + foregroundAlpha = npForeground[:, :, 3] * opacity + combinedAlpha = backgroundAlpha * foregroundAlpha + + # Get the colour from the layers + backgroundColor = npBackground[:, :, 0:3] + foregroundColor = npForeground[:, :, 0:3] + + # Some effects require alpha + alphaFunc = { + BlendType.DESTIN: destin, + BlendType.DESTOUT: destout, + BlendType.SRCATOP: srcatop, + BlendType.DESTATOP: destatop, + } + + if blendType in alphaFunc: + return Image.fromarray( + imageFloatToInt( + np.clip( + np.dstack( + alphaFunc[blendType]( + backgroundAlpha, foregroundAlpha, backgroundColor, foregroundColor + ) + ), + a_min=0, + a_max=1, + ) + ) + ) + + # Get the colours and the alpha for the new image + colorComponents = ( + (backgroundAlpha - combinedAlpha)[:, :, None] * backgroundColor + + (foregroundAlpha - combinedAlpha)[:, :, None] * foregroundColor + + combinedAlpha[:, :, None] * blend(backgroundColor, foregroundColor, blendType) + ) + alphaComponent = backgroundAlpha + foregroundAlpha - combinedAlpha + + return Image.fromarray( + imageFloatToInt(np.clip(np.dstack((colorComponents, alphaComponent)), a_min=0, a_max=1)) + ) diff --git a/venv/Lib/site-packages/blendmodes/blendtype.py b/venv/Lib/site-packages/blendmodes/blendtype.py new file mode 100644 index 00000000..1bde12a6 --- /dev/null +++ b/venv/Lib/site-packages/blendmodes/blendtype.py @@ -0,0 +1,72 @@ +"""Specify supported blend types.""" + +from __future__ import annotations + +from aenum import MultiValueEnum + + +class BlendType(str, MultiValueEnum): + """Specify supported blend types. + + NORMAL = "bm:normal", "normal" + MULTIPLY = "bm:multiply", "multiply" + ADDITIVE = "bm:additive", "additive" + COLOURBURN = "bm:colourburn", "colourburn" + COLOURDODGE = "bm:colourdodge", "colourdodge" + REFLECT = "bm:reflect", "reflect" + GLOW = "bm:glow", "glow" + OVERLAY = "bm:overlay", "overlay" + DIFFERENCE = "bm:difference", "difference" + NEGATION = "bm:negation", "negation" + LIGHTEN = "bm:lighten", "lighten" + DARKEN = "bm:darken", "darken" + SCREEN = "bm:screen", "screen" + XOR = "bm:xor", "xor" + SOFTLIGHT = "bm:softlight", "softlight" + HARDLIGHT = "bm:hardlight", "hardlight" + GRAINEXTRACT = "bm:grainextract", "grainextract" + GRAINMERGE = "bm:grainmerge", "grainmerge" + DIVIDE = "bm:divide", "divide" + HUE = "bm:hue", "hue" + SATURATION = "bm:saturation", "saturation" + COLOUR = "bm:colour", "colour" + LUMINOSITY = "bm:luminosity", "luminosity" + PINLIGHT = "bm:pinlight", "pinlight" + VIVIDLIGHT = "bm:vividlight", "vividlight" + EXCLUSION = "bm:exclusion", "exclusion" + DESTIN = "bm:destin", "destin" + DESTOUT = "bm:destout", "destout" + SRCATOP = "bm:srcatop", "srcatop" + DESTATOP = "bm:destatop", "destatop" + """ + + NORMAL = "bm:normal", "normal" + MULTIPLY = "bm:multiply", "multiply" + ADDITIVE = "bm:additive", "additive" + COLOURBURN = "bm:colourburn", "colourburn" + COLOURDODGE = "bm:colourdodge", "colourdodge" + REFLECT = "bm:reflect", "reflect" + GLOW = "bm:glow", "glow" + OVERLAY = "bm:overlay", "overlay" + DIFFERENCE = "bm:difference", "difference" + NEGATION = "bm:negation", "negation" + LIGHTEN = "bm:lighten", "lighten" + DARKEN = "bm:darken", "darken" + SCREEN = "bm:screen", "screen" + XOR = "bm:xor", "xor" + SOFTLIGHT = "bm:softlight", "softlight" + HARDLIGHT = "bm:hardlight", "hardlight" + GRAINEXTRACT = "bm:grainextract", "grainextract" + GRAINMERGE = "bm:grainmerge", "grainmerge" + DIVIDE = "bm:divide", "divide" + HUE = "bm:hue", "hue" + SATURATION = "bm:saturation", "saturation" + COLOUR = "bm:colour", "colour" + LUMINOSITY = "bm:luminosity", "luminosity" + PINLIGHT = "bm:pinlight", "pinlight" + VIVIDLIGHT = "bm:vividlight", "vividlight" + EXCLUSION = "bm:exclusion", "exclusion" + DESTIN = "bm:destin", "destin" + DESTOUT = "bm:destout", "destout" + SRCATOP = "bm:srcatop", "srcatop" + DESTATOP = "bm:destatop", "destatop" diff --git a/venv/Lib/site-packages/blendmodes/imagetools.py b/venv/Lib/site-packages/blendmodes/imagetools.py new file mode 100644 index 00000000..834b7c86 --- /dev/null +++ b/venv/Lib/site-packages/blendmodes/imagetools.py @@ -0,0 +1,48 @@ +"""Do stuff to images to prepare them. +""" +from __future__ import annotations + +import warnings + +from deprecation import deprecated +from PIL import Image + + +@deprecated(deprecated_in="2021.1", removed_in="", details="use renderWAlphaOffset") +def rasterImageOA( # pylint:disable=missing-function-docstring + image: Image.Image, size: tuple[int, int], alpha: float = 1.0, offsets: tuple[int, int] = (0, 0) +) -> Image.Image: + warnings.warn( + "Call to deprecated function rasterImageOA.", category=DeprecationWarning, stacklevel=2 + ) + return renderWAlphaOffset(image, size, alpha, offsets) + + +@deprecated(deprecated_in="2021.1", removed_in="", details="use renderWAlphaOffset") +def rasterImageOffset( # pylint:disable=missing-function-docstring + image: Image.Image, size: tuple[int, int], offsets: tuple[int, int] = (0, 0) +) -> Image.Image: + warnings.warn( + "Call to deprecated function rasterImageOffset.", category=DeprecationWarning, stacklevel=2 + ) + return renderWAlphaOffset(image, size, 1, offsets) + + +def renderWAlphaOffset( + image: Image.Image, size: tuple[int, int], alpha: float = 1.0, offsets: tuple[int, int] = (0, 0) +) -> Image.Image: + """Render an image with offset and alpha to a given size. + + Args: + image (Image.Image): pil image to draw + size (tuple[int, int]): width, height as a tuple + alpha (float, optional): alpha transparency. Defaults to 1.0. + offsets (tuple[int, int], optional): x, y offsets as a tuple. + Defaults to (0, 0). + + Returns: + Image.Image: new image + """ + imageOffset = Image.new("RGBA", size) + imageOffset.paste(image.convert("RGBA"), offsets, image.convert("RGBA")) + return Image.blend(Image.new("RGBA", size), imageOffset, alpha) From 2e8b5418e3cd4e9212f2fcdb36305d7a40f97916 Mon Sep 17 00:00:00 2001 From: ThereforeGames <95403634+ThereforeGames@users.noreply.github.com> Date: Sun, 11 Dec 2022 18:03:36 -0500 Subject: [PATCH 14/59] Improve color correction with luminosity blend --- modules/processing.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/processing.py b/modules/processing.py index 24c537d1..bc841837 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -27,6 +27,7 @@ from ldm.data.util import AddMiDaS from ldm.models.diffusion.ddpm import LatentDepth2ImageDiffusion from einops import repeat, rearrange +from blendmodes.blend import blendLayers, BlendType # some of those options should not be changed at all because they would break the model, so I removed them from options. opt_C = 4 @@ -39,17 +40,19 @@ def setup_color_correction(image): return correction_target -def apply_color_correction(correction, image): +def apply_color_correction(correction, original_image): logging.info("Applying color correction.") image = Image.fromarray(cv2.cvtColor(exposure.match_histograms( cv2.cvtColor( - np.asarray(image), + np.asarray(original_image), cv2.COLOR_RGB2LAB ), correction, channel_axis=2 ), cv2.COLOR_LAB2RGB).astype("uint8")) - + + image = blendLayers(image, original_image, BlendType.LUMINOSITY) + return image From 9170224d230fb22387e955d0b40ceda116f688b6 Mon Sep 17 00:00:00 2001 From: ThereforeGames <95403634+ThereforeGames@users.noreply.github.com> Date: Sun, 11 Dec 2022 18:06:22 -0500 Subject: [PATCH 15/59] Delete venv/Lib/site-packages directory --- venv/Lib/site-packages/aenum/CHANGES | 489 -- venv/Lib/site-packages/aenum/LICENSE | 32 - venv/Lib/site-packages/aenum/__init__.py | 4111 ---------- .../aenum/__pycache__/__init__.cpython-39.pyc | Bin 104964 -> 0 bytes .../aenum/__pycache__/_py3.cpython-39.pyc | Bin 686 -> 0 bytes venv/Lib/site-packages/aenum/_py2.py | 7 - venv/Lib/site-packages/aenum/_py3.py | 12 - venv/Lib/site-packages/aenum/doc/aenum.rst | 1568 ---- venv/Lib/site-packages/aenum/test.py | 6832 ----------------- venv/Lib/site-packages/aenum/test_v3.py | 1982 ----- venv/Lib/site-packages/blendmodes/__init__.py | 2 - .../__pycache__/__init__.cpython-39.pyc | Bin 285 -> 0 bytes .../__pycache__/blend.cpython-39.pyc | Bin 13302 -> 0 bytes .../__pycache__/blendtype.cpython-39.pyc | Bin 2850 -> 0 bytes venv/Lib/site-packages/blendmodes/blend.py | 511 -- .../Lib/site-packages/blendmodes/blendtype.py | 72 - .../site-packages/blendmodes/imagetools.py | 48 - 17 files changed, 15666 deletions(-) delete mode 100644 venv/Lib/site-packages/aenum/CHANGES delete mode 100644 venv/Lib/site-packages/aenum/LICENSE delete mode 100644 venv/Lib/site-packages/aenum/__init__.py delete mode 100644 venv/Lib/site-packages/aenum/__pycache__/__init__.cpython-39.pyc delete mode 100644 venv/Lib/site-packages/aenum/__pycache__/_py3.cpython-39.pyc delete mode 100644 venv/Lib/site-packages/aenum/_py2.py delete mode 100644 venv/Lib/site-packages/aenum/_py3.py delete mode 100644 venv/Lib/site-packages/aenum/doc/aenum.rst delete mode 100644 venv/Lib/site-packages/aenum/test.py delete mode 100644 venv/Lib/site-packages/aenum/test_v3.py delete mode 100644 venv/Lib/site-packages/blendmodes/__init__.py delete mode 100644 venv/Lib/site-packages/blendmodes/__pycache__/__init__.cpython-39.pyc delete mode 100644 venv/Lib/site-packages/blendmodes/__pycache__/blend.cpython-39.pyc delete mode 100644 venv/Lib/site-packages/blendmodes/__pycache__/blendtype.cpython-39.pyc delete mode 100644 venv/Lib/site-packages/blendmodes/blend.py delete mode 100644 venv/Lib/site-packages/blendmodes/blendtype.py delete mode 100644 venv/Lib/site-packages/blendmodes/imagetools.py diff --git a/venv/Lib/site-packages/aenum/CHANGES b/venv/Lib/site-packages/aenum/CHANGES deleted file mode 100644 index e8799f6d..00000000 --- a/venv/Lib/site-packages/aenum/CHANGES +++ /dev/null @@ -1,489 +0,0 @@ -3.1.8 -===== - -recalculate bits used after all flags created (sometimes needed when a custom -`__new__` is in place. - - -3.1.7 -===== - -update flag creation to (possibly) add bitwise operator methods to newly -created flags - -update extend_enum() to work with 3.11 flags - - -3.1.6 -===== - -Update `dir()` on mixed enums to include mixed data type methods and -attributes. - -Rename `enum_property` to `property` to match stdlib. Recommended usage is -`aenum.property` (prefix with module name). - -Remove quadritic creation behavior. - - -BREAKING CHANGE BUG FIX that won't affect most people - -Enums with a custom `__new__` that: - -- use the enum machinery to generate the values; AND -- have keyword arguments set to a default (like `None`) - -will fail to generate a missing value. To fix: remove the default value and -instead specify it on the member creation line. - -BREAKING CHANGE - -In Python 3.11 the `str()` of mixed enums will now match its `format()` which -will be the normal `str()` of the data type -- so for an IntEnum you'll see -`5` instead of `Perm.R|X`. This affects IntEnum, StrEnum, and IntFlag. - - -3.1.5 -===== - -fix support of `auto()` kwds - - -3.1.3 -===== - -rename `aenum.property` to `aenum.enum_property` - -fix `enum_property` to work with `_init_` attributes - - -3.1.2 -===== - -fix `extend_enum()` for unhashable values - - -3.1.1 -===== - -fix `extend_enum()` for most cases - - -3.1.0 -===== - -AddValue is similar to the old AutoNumber: it will always activate, but -uses _generate_next_value_ to get the next value (so the user has some -control over the return data type instead of always getting an int). - - -BREAKING CHANGES - -AutoValue is gone. It was superflous and its removal simplified the code. -Simply put the fields needed in an `_init_` and `_generate_next_value_` -will be called to supply the missing values (this is probably already what -is happening). - - - -3.0.0 -===== - -standard Enum usage is unchanged - -BREAKING CHANGES - -Enum -- the more esoteric method of creating Enums have been modified or removed - - AutoNumber setting is gone, inherit from AutoNumberEnum instead - - creating members without specifying anything is removed (if you don't - know what this means, you weren't doing it) - -Flag -- unique flags are canonical (i.e. flags with powers of two values such as - 1, 2, 4, 8, 16, etc.) -- non-unique flags are aliases (i.e. values such as 3 or 7) -- iteration of Flag and flag members only uses canonical flags - - -ENHANCEMENTS - -Member creation has been redone to match Python 3.10's methods. This also -allows all supported Pythons (2.7, 3.3+) to use the __set_name__ and -__init_subclass__ protocols (more robustly than in aenum 2.2.5) - - -CHANGES - -enum_property() has been renamed to property() (old name still available, but -deprecated). - -bin() replacement shows negative integers in twos-complement - - - - -2.2.5 -===== - -call __init_subclass__ after members have been added, and in Pythons < 3.6 -call __set_name__ in Pythons < 3.6 -do not convert/disallow private names -add iteration/len support to NamedConstant - - -2.2.4 -===== - -add support to Constant to retrieve members by value - - --> class K(Constant): - ... one = 1 - ... two = 2 - - --> K.one - - - --> K(1) - - -add pickle/deepcopy support to Constant - -add support for Constant to use other Constant values - (resulting members /are not/ the same) - - --> class C(Constant) - ... one = K.one - ... three = 3 - - --> C.one == K.one - True - - --> C.one is K.one - False - -AutoNumber and auto() now work together - -Enum members are now added to the class as enum_property, which supports -unshadowing of parent class attributes when called on an Enum member: - - --> class StrEnum(str, Enum): - ... lower = 'lower' - ... upper = 'upper' - ... mixed = 'mixed' - - --> StrEnum.lower - - - --> StrEnum.lower.upper() - 'LOWER' - - --> StrEnum.upper - - - --> StrEnum.upper.upper() - 'UPPER' - - -2.2.3 -===== - -use members' type's methods __str__, __repr__, __format__, and -__reduce_ex__ if directly assigned in Enum class body; i.e.: - - --> class Color(str, Enum): - ... red = 'red' - ... green = 'green' - ... blue = 'blue' - ... __str__ = str.__str__ - - --> print(repr(Color.green)) - - - --> print(Color.green) - green - - -2.2.2 -===== - -replace _RouteClassAttributeToGetattr with enum_property (it is still -available as an alias) - -support constant() and auto() being used together: - - --> class Fruit(Flag): - ... _order_ = 'apple banana lemon orange' - ... apple = auto() - ... banana = auto() - ... lemon = auto() - ... orange = auto() - ... CitrusTypes = constant(lemon | orange) - - --> list(Fruit) - [Fruit.apple, Fruit.banana, Fruit.lemon, Fruit.orange] - - --> list(Fruit.CitrusTypes) - [Fruit.orange, Fruit.lemon] - - --> Fruit.orange in Fruit.CitrusTypes - True - - -2.2.1 -===== - -allow Enums to be called without a value - - class Color(Enum): - black = 0 - red = 1 - green = 2 - blue = 3 - # - @classmethod - def _missing_value_(cls, value): - if value is no_arg: - return cls.black - - >>> Color() - - -allow Enum name use while constructing Enum (Python 3.4+ only) - - --> class Color(Enum): - ... _order_ = 'BLACK WHITE' - ... BLACK = Color('black', '#000') - ... WHITE = Color('white', '#fff') - ... # - ... def __init__(self, label, hex): - ... self.label = label - ... self.hex = hex - - -2.2.0 -===== - -BREAKING CHANGE ---------------- -In Python 3+ classes defined inside an Enum no longer become members by -default; in Python 2 they still become members, but see below. - -For cross-compatibility and full control two decorators are provided: - -- @member --> forces item to become a member -- @nonmember --> excludes item from becoming a member - -So to have an Enum that behaves the same in Python 2 and 3, use the -decorators (and other compatibility shims): - - class Color(Enum): - - _order_ = 'red green blue' - - red = 1 - green = 2 - blue = 3 - - @nonmember - class Shades(Enum): - - _order_ = 'light medium dark' - - light = 1 - medium = 2 - dark = 3 - - -2.1.4 -===== - -EnumMeta: -- change __member_new__ to __new_member__ (as the stdlib enum does) -- assign member name to enum() instances (an Enum helper for defining members) -- handle empty iterables when using functional API -- make auto() work with previous enum members -- keep searching mixins until base class is found - -Enum: -- fix bug in Flag checks (ensure it is a Flag before checking the name) -- add multiple mixin support -- do not allow blank names (functional API) -- raise TypeError if _missing_* returns wrong type -- fix __format__ to honor custom __str__ - -extend_enum: -- support stdlib Enums -- use _generate_next_value_ if value not provided - -general: -- standardize exception formatting -- use getfullargspec() in Python 3 (avoids deprecation warnings) - - -2.1.2 -===== - -when order is callable, save it for subclass use - - -2.1.1 -===== - -correctly raise TypeError for non-Enum containment checks -support combining names with | for Flag key access -support _order_ being a callable - - -2.1.0 -===== - -support Flags being combined with other data types: -- add _create_pseudo_member_values_ -- add default __new__ and temporary _init_ - - -2.0.10 -====== - -ensure _ignore_ is set when _settings_ specified in body which includes -AutoValue - -make Flag members iterable - - -2.0.9 -===== - -fix missing comma in __all__ -fix extend_enum with custom __new__ methods -fix MultiValue with AutoNumber without _init_ - - -2.0.8 -===== - -extend_enum now handles aliases and multivalues correctly - - -2.0.7 -===== - -support mixin types with extend_enum -init and AutoNumber can now work together -add test for new Enum using EnumMeta -add tests for variations of multivalue and init -prevent deletion of NamedConstant.constant - - -2.0.6 -===== - -constants cannot be deleted (they already couldn't be changed) -constants can be used to define other constants - - -2.0.5 -===== - -_init_ and MultiValue can now work together - - -2.0.4 -===== - -_init_ and AutoValue (and _generate_next_value_) can now work together to -supply missing values even when some of the required values per member are -absent - - -2.0.3 -===== - -add _missing_value_ and _missing_name_ methods, deprecate _missing_ -make enum instances comparable - - -2.0.2 -===== - -both EnumMeta.__getattr__ and Enum.__new__ fall back to _missing_ - - -2.0.1 -===== - -auto() now works with other data types -AutoNumber supports legacy Enums (fixed regression) - - -2.0.0 -===== - -Flag and IntFlag added. - - -1.4.7 -===== - -fix %-interpolation bug -defined SqlLiteEnum only if sqlite exists -support pyflakes - - -1.4.6 -===== - -version numbering error - - -1.4.5 -===== - -revert AutoNumberEnum to custom __new__ instead of AutoNumber -use _ignore_ to shield against AutoNumber magic -inherit start and init settings from base Enums - - -1.4.4 -===== - -enabled export as a decorator -enabled _order_ to replace __order__ -enabled python2 support for settings, init, and start - - -1.4.3 -===== - -support _ignore_ for dynamically creating class bodies - - -1.4.2 -===== - -MultiValue, NoAlias, Unique, and init now work with Python 2 - - -1.4.1 -===== - -Py3: added Enum creation flags: Auto, MultiValue, NoAlias, Unique - -fixed extend_enum to honor Enum flags - - -1.4.0 -===== - -When possible aenum inherits from Python's own enum. - -Breaking change: enum members now default to evaluating as True to maintain -compatibility with the stdlib. - -Add your own __bool__ (__nonzero__ in Python 2) if need this behavior: - - def __bool__(self): - return bool(self.value) - __nonzero__ = __bool__ - diff --git a/venv/Lib/site-packages/aenum/LICENSE b/venv/Lib/site-packages/aenum/LICENSE deleted file mode 100644 index b20361ce..00000000 --- a/venv/Lib/site-packages/aenum/LICENSE +++ /dev/null @@ -1,32 +0,0 @@ -Copyright (c) 2015, 2016, 2017, 2018 Ethan Furman. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - - Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials - provided with the distribution. - - Neither the name Ethan Furman nor the names of any - contributors may be used to endorse or promote products - derived from this software without specific prior written - permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/Lib/site-packages/aenum/__init__.py b/venv/Lib/site-packages/aenum/__init__.py deleted file mode 100644 index 26c504cc..00000000 --- a/venv/Lib/site-packages/aenum/__init__.py +++ /dev/null @@ -1,4111 +0,0 @@ -"""Python Advanced Enumerations & NameTuples""" -from __future__ import print_function - -# imports -import sys as _sys -pyver = _sys.version_info[:2] -PY2 = pyver < (3, ) -PY3 = pyver >= (3, ) -PY2_6 = (2, 6) -PY3_3 = (3, 3) -PY3_4 = (3, 4) -PY3_5 = (3, 5) -PY3_6 = (3, 6) -PY3_11 = (3, 11) - -import re - -_bltin_property = property -_bltin_bin = bin - -try: - from collections import OrderedDict -except ImportError: - OrderedDict = dict -from collections import defaultdict -try: - import sqlite3 -except ImportError: - sqlite3 = None - -try: - RecursionError -except NameError: - # python3.4 - RecursionError = RuntimeError - -from operator import or_ as _or_, and_ as _and_, xor as _xor_, inv as _inv_ -from operator import abs as _abs_, add as _add_, floordiv as _floordiv_ -from operator import lshift as _lshift_, rshift as _rshift_, mod as _mod_ -from operator import mul as _mul_, neg as _neg_, pos as _pos_, pow as _pow_ -from operator import truediv as _truediv_, sub as _sub_ - -if PY2: - from ._py2 import * -if PY3: - from ._py3 import * - -obj_type = type - -__all__ = [ - 'NamedConstant', 'constant', 'skip', 'nonmember', 'member', 'no_arg', - 'Enum', 'IntEnum', 'AutoNumberEnum', 'OrderedEnum', 'UniqueEnum', - 'StrEnum', 'UpperStrEnum', 'LowerStrEnum', - 'Flag', 'IntFlag', - 'AddValue', 'MagicValue', 'MultiValue', 'NoAlias', 'Unique', - 'AddValueEnum', 'MultiValueEnum', 'NoAliasEnum', - 'enum', 'extend_enum', 'unique', 'property', - 'NamedTuple', 'SqliteEnum', - 'FlagBoundary', 'STRICT', 'CONFORM', 'EJECT', 'KEEP', - 'add_stdlib_integration', 'remove_stdlib_integration' - ] - -if sqlite3 is None: - __all__.remove('SqliteEnum') - -version = 3, 1, 12, 1 - -# shims -try: - any -except NameError: - def any(iterable): - for element in iterable: - if element: - return True - return False - -try: - unicode - unicode = unicode -except NameError: - # In Python 3 unicode no longer exists (it's just str) - unicode = str - -try: - basestring - basestring = bytes, unicode -except NameError: - # In Python 2 basestring is the ancestor of both str and unicode - # in Python 3 it's just str, but was missing in 3.1 - basestring = str, - -try: - long - baseinteger = int, long -except NameError: - baseinteger = int, - long = int -# deprecated -baseint = baseinteger - -try: - NoneType -except NameError: - NoneType = type(None) - -try: - # derive from stdlib enum if possible - import enum - if hasattr(enum, 'version'): - raise ImportError('wrong version') - else: - from enum import EnumMeta as StdlibEnumMeta, Enum as StdlibEnum, IntEnum as StdlibIntEnum - StdlibFlag = StdlibIntFlag = StdlibStrEnum = StdlibReprEnum = None -except ImportError: - StdlibEnumMeta = StdlibEnum = StdlibIntEnum = StdlibIntFlag = StdlibFlag = StdlibStrEnum = None - -if StdlibEnum: - try: - from enum import IntFlag as StdlibIntFlag, Flag as StdlibFlag - except ImportError: - pass - try: - from enum import StrEnum as StdlibStrEnum - except ImportError: - pass - try: - from enum import ReprEnum as StdlibReprEnum - except ImportError: - pass - - - - -# helpers -# will be exported later -MagicValue = AddValue = MultiValue = NoAlias = Unique = None - -def _bit_count(num): - """ - return number of set bits - - Counting bits set, Brian Kernighan's way* - - unsigned int v; // count the number of bits set in v - unsigned int c; // c accumulates the total bits set in v - for (c = 0; v; c++) - { v &= v - 1; } //clear the least significant bit set - - This method goes through as many iterations as there are set bits. So if we - have a 32-bit word with only the high bit set, then it will only go once - through the loop. - - * The C Programming Language 2nd Ed., Kernighan & Ritchie, 1988. - - This works because each subtraction "borrows" from the lowest 1-bit. For - example: - - loop pass 1 loop pass 2 - ----------- ----------- - 101000 100000 - - 1 - 1 - = 100111 = 011111 - & 101000 & 100000 - = 100000 = 0 - - It is an excellent technique for Python, since the size of the integer need - not be determined beforehand. - - (from https://wiki.python.org/moin/BitManipulation) - """ - count = 0 - while num: - num &= num - 1 - count += 1 - return count - -def _is_single_bit(value): - """ - True if only one bit set in value (should be an int) - """ - if value == 0: - return False - value &= value - 1 - return value == 0 - -def _iter_bits_lsb(value): - """ - Return each bit value one at a time. - - >>> list(_iter_bits_lsb(6)) - [2, 4] - """ - - while value: - bit = value & (~value + 1) - yield bit - value ^= bit - -def bin(value, max_bits=None): - """ - Like built-in bin(), except negative values are represented in - twos-compliment, and the leading bit always indicates sign - (0=positive, 1=negative). - - >>> bin(10) - '0b0 1010' - >>> bin(~10) # ~10 is -11 - '0b1 0101' - """ - - ceiling = 2 ** (value).bit_length() - if value >= 0: - s = _bltin_bin(value + ceiling).replace('1', '0', 1) - else: - s = _bltin_bin(~value ^ (ceiling - 1) + ceiling) - sign = s[:3] - digits = s[3:] - if max_bits is not None: - if len(digits) < max_bits: - digits = (sign[-1] * max_bits + digits)[-max_bits:] - return "%s %s" % (sign, digits) - - -try: - from types import DynamicClassAttribute - base = DynamicClassAttribute -except ImportError: - base = object - DynamicClassAttribute = None - -class property(base): - """ - This is a descriptor, used to define attributes that act differently - when accessed through an enum member and through an enum class. - Instance access is the same as property(), but access to an attribute - through the enum class will look in the class' _member_map_. - """ - - # inherit from DynamicClassAttribute if we can in order to get `inspect` - # support - - def __init__(self, fget=None, fset=None, fdel=None, doc=None): - self.fget = fget - self.fset = fset - self.fdel = fdel - # next two lines make property act the same as _bltin_property - self.__doc__ = doc or fget.__doc__ - self.overwrite_doc = doc is None - # support for abstract methods - self.__isabstractmethod__ = bool(getattr(fget, '__isabstractmethod__', False)) - # names, if possible - - def getter(self, fget): - fdoc = fget.__doc__ if self.overwrite_doc else None - result = type(self)(fget, self.fset, self.fdel, fdoc or self.__doc__) - result.overwrite_doc = self.__doc__ is None - return result - - def setter(self, fset): - fdoc = fget.__doc__ if self.overwrite_doc else None - result = type(self)(self.fget, fset, self.fdel, self.__doc__) - result.overwrite_doc = self.__doc__ is None - return result - - def deleter(self, fdel): - fdoc = fget.__doc__ if self.overwrite_doc else None - result = type(self)(self.fget, self.fset, fdel, self.__doc__) - result.overwrite_doc = self.__doc__ is None - return result - - def __repr__(self): - member = self.ownerclass._member_map_.get(self.name) - func = self.fget or self.fset or self.fdel - strings = [] - if member: - strings.append('%r' % member) - if func: - strings.append('function=%s' % func.__name__) - return 'property(%s)' % ', '.join(strings) - - def __get__(self, instance, ownerclass=None): - if instance is None: - try: - return ownerclass._member_map_[self.name] - except KeyError: - raise AttributeError( - '%r has no attribute %r' % (ownerclass, self.name) - ) - else: - if self.fget is not None: - return self.fget(instance) - else: - if self.fset is not None: - raise AttributeError( - 'cannot read attribute %r on %r' % (self.name, ownerclass) - ) - else: - try: - return instance.__dict__[self.name] - except KeyError: - raise AttributeError( - '%r member has no attribute %r' % (ownerclass, self.name) - ) - - def __set__(self, instance, value): - if self.fset is None: - if self.fget is not None: - raise AttributeError( - "cannot set attribute %r on " % (self.name, self.clsname) - ) - else: - instance.__dict__[self.name] = value - else: - return self.fset(instance, value) - - def __delete__(self, instance): - if self.fdel is None: - if self.fget or self.fset: - raise AttributeError( - "cannot delete attribute %r on " % (self.name, self.clsname) - ) - elif self.name in instance.__dict__: - del instance.__dict__[self.name] - else: - raise AttributeError( - "no attribute %r on member" % (self.name, self.clsname) - ) - else: - return self.fdel(instance) - - def __set_name__(self, ownerclass, name): - self.name = name - self.clsname = ownerclass.__name__ - self.ownerclass = ownerclass - -_RouteClassAttributeToGetattr = property -if DynamicClassAttribute is None: - DynamicClassAttribute = property -# deprecated -enum_property = property - -class NonMember(object): - """ - Protects item from becaming an Enum member during class creation. - """ - def __init__(self, value): - self.value = value - - def __get__(self, instance, ownerclass=None): - return self.value -skip = nonmember = NonMember - -class Member(object): - """ - Forces item to became an Enum member during class creation. - """ - def __init__(self, value): - self.value = value -member = Member - -class SentinelType(type): - def __repr__(cls): - return '<%s>' % cls.__name__ -Sentinel = SentinelType('Sentinel', (object, ), {}) - -def _is_descriptor(obj): - """Returns True if obj is a descriptor, False otherwise.""" - return ( - hasattr(obj, '__get__') or - hasattr(obj, '__set__') or - hasattr(obj, '__delete__')) - - -def _is_dunder(name): - """Returns True if a __dunder__ name, False otherwise.""" - return (len(name) > 4 and - name[:2] == name[-2:] == '__' and - name[2] != '_' and - name[-3] != '_') - - -def _is_sunder(name): - """Returns True if a _sunder_ name, False otherwise.""" - return (len(name) > 2 and - name[0] == name[-1] == '_' and - name[1] != '_' and - name[-2] != '_') - -def _is_internal_class(cls_name, obj): - # only 3.3 and up, always return False in 3.2 and below - if pyver < PY3_3: - return False - else: - qualname = getattr(obj, '__qualname__', False) - return not _is_descriptor(obj) and qualname and re.search(r"\.?%s\.\w+$" % cls_name, qualname) - -def _is_private_name(cls_name, name): - pattern = r'^_%s__\w+[^_]_?$' % (cls_name, ) - return re.search(pattern, name) - -def _power_of_two(value): - if value < 1: - return False - return value == 2 ** _high_bit(value) - -def bits(num): - if num in (0, 1): - return str(num) - negative = False - if num < 0: - negative = True - num = ~num - result = bits(num>>1) + str(num&1) - if negative: - result = '1' + ''.join(['10'[d=='1'] for d in result]) - return result - - -def bit_count(num): - """ - return number of set bits - - Counting bits set, Brian Kernighan's way* - - unsigned int v; // count the number of bits set in v - unsigned int c; // c accumulates the total bits set in v - for (c = 0; v; c++) - { v &= v - 1; } //clear the least significant bit set - - This method goes through as many iterations as there are set bits. So if we - have a 32-bit word with only the high bit set, then it will only go once - through the loop. - - * The C Programming Language 2nd Ed., Kernighan & Ritchie, 1988. - - This works because each subtraction "borrows" from the lowest 1-bit. For example: - - loop pass 1 loop pass 2 - ----------- ----------- - 101000 100000 - - 1 - 1 - = 100111 = 011111 - & 101000 & 100000 - = 100000 = 0 - - It is an excellent technique for Python, since the size of the integer need not - be determined beforehand. - """ - count = 0 - while(num): - num &= num - 1 - count += 1 - return(count) - -def bit_len(num): - length = 0 - while num: - length += 1 - num >>= 1 - return length - -def is_single_bit(num): - """ - True if only one bit set in num (should be an int) - """ - num &= num - 1 - return num == 0 - -def _make_class_unpicklable(obj): - """ - Make the given obj un-picklable. - - obj should be either a dictionary, on an Enum - """ - def _break_on_call_reduce(self, proto): - raise TypeError('%r cannot be pickled' % self) - if isinstance(obj, dict): - obj['__reduce_ex__'] = _break_on_call_reduce - obj['__module__'] = '' - else: - setattr(obj, '__reduce_ex__', _break_on_call_reduce) - setattr(obj, '__module__', '') - -def _check_auto_args(method): - """check if new generate method supports *args and **kwds""" - if isinstance(method, staticmethod): - method = method.__get__(type) - method = getattr(method, 'im_func', method) - args, varargs, keywords, defaults = getargspec(method) - return varargs is not None and keywords is not None - -def _get_attr_from_chain(cls, attr): - sentinel = object() - for basecls in cls.mro(): - obj = basecls.__dict__.get(attr, sentinel) - if obj is not sentinel: - return obj - -def _value(obj): - if isinstance(obj, (auto, constant)): - return obj.value - else: - return obj - -def enumsort(things): - """ - sorts things by value if all same type; otherwise by name - """ - if not things: - return things - sort_type = type(things[0]) - if not issubclass(sort_type, tuple): - # direct sort or type error - if not all((type(v) is sort_type) for v in things[1:]): - raise TypeError('cannot sort items of different types') - return sorted(things) - else: - # expecting list of (name, value) tuples - sort_type = type(things[0][1]) - try: - if all((type(v[1]) is sort_type) for v in things[1:]): - return sorted(things, key=lambda i: i[1]) - else: - raise TypeError('try name sort instead') - except TypeError: - return sorted(things, key=lambda i: i[0]) - -def export(collection, namespace=None): - """ - export([collection,] namespace) -> Export members to target namespace. - - If collection is not given, act as a decorator. - """ - if namespace is None: - namespace = collection - def export_decorator(collection): - return export(collection, namespace) - return export_decorator - elif issubclass(collection, NamedConstant): - for n, c in collection.__dict__.items(): - if isinstance(c, NamedConstant): - namespace[n] = c - elif issubclass(collection, Enum): - data = collection.__members__.items() - for n, m in data: - namespace[n] = m - else: - raise TypeError('%r is not a supported collection' % (collection,) ) - return collection - -class _Addendum(object): - def __init__(self, dict, doc, ns): - # dict is the dict to update with functions - # doc is the docstring to put in the dict - # ns is the namespace to remove the function names from - self.dict = dict - self.ns = ns - self.added = set() - def __call__(self, func): - if isinstance(func, (staticmethod, classmethod)): - name = func.__func__.__name__ - elif isinstance(func, (property, _bltin_property)): - name = (func.fget or func.fset or func.fdel).__name__ - else: - name = func.__name__ - self.dict[name] = func - self.added.add(name) - def resolve(self): - ns = self.ns - for name in self.added: - del ns[name] - return self.dict - -# Constant / NamedConstant - -class constant(object): - ''' - Simple constant descriptor for NamedConstant and Enum use. - ''' - def __init__(self, value, doc=None): - self.value = value - self.__doc__ = doc - - def __get__(self, *args): - return self.value - - def __repr__(self): - return '%s(%r)' % (self.__class__.__name__, self.value) - - def __and__(self, other): - return _and_(self.value, _value(other)) - - def __rand__(self, other): - return _and_(_value(other), self.value) - - def __invert__(self): - return _inv_(self.value) - - def __or__(self, other): - return _or_(self.value, _value(other)) - - def __ror__(self, other): - return _or_(_value(other), self.value) - - def __xor__(self, other): - return _xor_(self.value, _value(other)) - - def __rxor__(self, other): - return _xor_(_value(other), self.value) - - def __abs__(self): - return _abs_(self.value) - - def __add__(self, other): - return _add_(self.value, _value(other)) - - def __radd__(self, other): - return _add_(_value(other), self.value) - - def __neg__(self): - return _neg_(self.value) - - def __pos__(self): - return _pos_(self.value) - - if PY2: - def __div__(self, other): - return _div_(self.value, _value(other)) - - def __rdiv__(self, other): - return _div_(_value(other), (self.value)) - - def __floordiv__(self, other): - return _floordiv_(self.value, _value(other)) - - def __rfloordiv__(self, other): - return _floordiv_(_value(other), self.value) - - def __truediv__(self, other): - return _truediv_(self.value, _value(other)) - - def __rtruediv__(self, other): - return _truediv_(_value(other), self.value) - - def __lshift__(self, other): - return _lshift_(self.value, _value(other)) - - def __rlshift__(self, other): - return _lshift_(_value(other), self.value) - - def __rshift__(self, other): - return _rshift_(self.value, _value(other)) - - def __rrshift__(self, other): - return _rshift_(_value(other), self.value) - - def __mod__(self, other): - return _mod_(self.value, _value(other)) - - def __rmod__(self, other): - return _mod_(_value(other), self.value) - - def __mul__(self, other): - return _mul_(self.value, _value(other)) - - def __rmul__(self, other): - return _mul_(_value(other), self.value) - - def __pow__(self, other): - return _pow_(self.value, _value(other)) - - def __rpow__(self, other): - return _pow_(_value(other), self.value) - - def __sub__(self, other): - return _sub_(self.value, _value(other)) - - def __rsub__(self, other): - return _sub_(_value(other), self.value) - - def __set_name__(self, ownerclass, name): - self.name = name - self.clsname = ownerclass.__name__ - - -NamedConstant = None - -class _NamedConstantDict(dict): - """Track constant order and ensure names are not reused. - - NamedConstantMeta will use the names found in self._names as the - Constant names. - """ - def __init__(self): - super(_NamedConstantDict, self).__init__() - self._names = [] - - def __setitem__(self, key, value): - """Changes anything not dundered or not a constant descriptor. - - If an constant name is used twice, an error is raised; duplicate - values are not checked for. - - Single underscore (sunder) names are reserved. - """ - if _is_sunder(key): - raise ValueError( - '_sunder_ names, such as %r, are reserved for future NamedConstant use' - % (key, ) - ) - elif _is_dunder(key): - pass - elif key in self._names: - # overwriting an existing constant? - raise TypeError('attempt to reuse name: %r' % (key, )) - elif isinstance(value, constant) or not _is_descriptor(value): - if key in self: - # overwriting a descriptor? - raise TypeError('%s already defined as: %r' % (key, self[key])) - self._names.append(key) - super(_NamedConstantDict, self).__setitem__(key, value) - - -class NamedConstantMeta(type): - """ - Block attempts to reassign NamedConstant attributes. - """ - - @classmethod - def __prepare__(metacls, cls, bases, **kwds): - return _NamedConstantDict() - - def __new__(metacls, cls, bases, clsdict): - if type(clsdict) is dict: - original_dict = clsdict - clsdict = _NamedConstantDict() - for k, v in original_dict.items(): - clsdict[k] = v - newdict = {} - constants = {} - for name, obj in clsdict.items(): - if name in clsdict._names: - constants[name] = obj - continue - elif isinstance(obj, nonmember): - obj = obj.value - newdict[name] = obj - newcls = super(NamedConstantMeta, metacls).__new__(metacls, cls, bases, newdict) - newcls._named_constant_cache_ = {} - newcls._members_ = {} - for name, obj in constants.items(): - new_k = newcls.__new__(newcls, name, obj) - newcls._members_[name] = new_k - return newcls - - def __bool__(cls): - return True - - def __delattr__(cls, attr): - cur_obj = cls.__dict__.get(attr) - if NamedConstant is not None and isinstance(cur_obj, NamedConstant): - raise AttributeError('cannot delete constant <%s.%s>' % (cur_obj.__class__.__name__, cur_obj._name_)) - super(NamedConstantMeta, cls).__delattr__(attr) - - def __iter__(cls): - return (k for k in cls._members_.values()) - - def __reversed__(cls): - return (k for k in reversed(cls._members_.values())) - - def __len__(cls): - return len(cls._members_) - - __nonzero__ = __bool__ - - def __setattr__(cls, name, value): - """Block attempts to reassign NamedConstants. - """ - cur_obj = cls.__dict__.get(name) - if NamedConstant is not None and isinstance(cur_obj, NamedConstant): - raise AttributeError('cannot rebind constant <%s.%s>' % (cur_obj.__class__.__name__, cur_obj._name_)) - super(NamedConstantMeta, cls).__setattr__(name, value) - -constant_dict = _Addendum( - dict=NamedConstantMeta.__prepare__('NamedConstant', (object, )), - doc="NamedConstants protection.\n\n Derive from this class to lock NamedConstants.\n\n", - ns=globals(), - ) - -@constant_dict -def __new__(cls, name, value=None, doc=None): - if value is None: - # lookup, name is value - value = name - for name, obj in cls.__dict__.items(): - if isinstance(obj, cls) and obj._value_ == value: - return obj - else: - raise ValueError('%r does not exist in %r' % (value, cls.__name__)) - cur_obj = cls.__dict__.get(name) - if isinstance(cur_obj, NamedConstant): - raise AttributeError('cannot rebind constant <%s.%s>' % (cur_obj.__class__.__name__, cur_obj._name_)) - elif isinstance(value, constant): - doc = doc or value.__doc__ - value = value.value - metacls = cls.__class__ - if isinstance(value, NamedConstant): - # constants from other classes are reduced to their actual value - value = value._value_ - actual_type = type(value) - value_type = cls._named_constant_cache_.get(actual_type) - if value_type is None: - value_type = type(cls.__name__, (cls, type(value)), {}) - cls._named_constant_cache_[type(value)] = value_type - obj = actual_type.__new__(value_type, value) - obj._name_ = name - obj._value_ = value - obj.__doc__ = doc - cls._members_[name] = obj - metacls.__setattr__(cls, name, obj) - return obj - -@constant_dict -def __repr__(self): - return "<%s.%s: %r>" % ( - self.__class__.__name__, self._name_, self._value_) - -@constant_dict -def __reduce_ex__(self, proto): - return getattr, (self.__class__, self._name_) - -NamedConstant = NamedConstantMeta('NamedConstant', (object, ), constant_dict.resolve()) -Constant = NamedConstant -del constant_dict - -# NamedTuple - -class _NamedTupleDict(OrderedDict): - """Track field order and ensure field names are not reused. - - NamedTupleMeta will use the names found in self._field_names to translate - to indices. - """ - def __init__(self, *args, **kwds): - self._field_names = [] - super(_NamedTupleDict, self).__init__(*args, **kwds) - - def __setitem__(self, key, value): - """Records anything not dundered or not a descriptor. - - If a field name is used twice, an error is raised. - - Single underscore (sunder) names are reserved. - """ - if _is_sunder(key): - if key not in ('_size_', '_order_', '_fields_'): - raise ValueError( - '_sunder_ names, such as %r, are reserved for future NamedTuple use' - % (key, ) - ) - elif _is_dunder(key): - if key == '__order__': - key = '_order_' - elif key in self._field_names: - # overwriting a field? - raise TypeError('attempt to reuse field name: %r' % (key, )) - elif not _is_descriptor(value): - if key in self: - # field overwriting a descriptor? - raise TypeError('%s already defined as: %r' % (key, self[key])) - self._field_names.append(key) - super(_NamedTupleDict, self).__setitem__(key, value) - - -class _TupleAttributeAtIndex(object): - - def __init__(self, name, index, doc, default): - self.name = name - self.index = index - if doc is undefined: - doc = None - self.__doc__ = doc - self.default = default - - def __get__(self, instance, owner): - if instance is None: - return self - if len(instance) <= self.index: - raise AttributeError('%s instance has no value for %s' % (instance.__class__.__name__, self.name)) - return instance[self.index] - - def __repr__(self): - return '%s(%d)' % (self.__class__.__name__, self.index) - - -class undefined(object): - def __repr__(self): - return 'undefined' - def __bool__(self): - return False - __nonzero__ = __bool__ -undefined = undefined() - - -class TupleSize(NamedConstant): - fixed = constant('fixed', 'tuple length is static') - minimum = constant('minimum', 'tuple must be at least x long (x is calculated during creation') - variable = constant('variable', 'tuple length can be anything') - -class NamedTupleMeta(type): - """Metaclass for NamedTuple""" - - @classmethod - def __prepare__(metacls, cls, bases, size=undefined, **kwds): - return _NamedTupleDict() - - def __init__(cls, *args , **kwds): - super(NamedTupleMeta, cls).__init__(*args) - - def __new__(metacls, cls, bases, clsdict, size=undefined, **kwds): - if bases == (object, ): - bases = (tuple, object) - elif tuple not in bases: - if object in bases: - index = bases.index(object) - bases = bases[:index] + (tuple, ) + bases[index:] - else: - bases = bases + (tuple, ) - # include any fields from base classes - base_dict = _NamedTupleDict() - namedtuple_bases = [] - for base in bases: - if isinstance(base, NamedTupleMeta): - namedtuple_bases.append(base) - i = 0 - if namedtuple_bases: - for name, index, doc, default in metacls._convert_fields(*namedtuple_bases): - base_dict[name] = index, doc, default - i = max(i, index) - # construct properly ordered dict with normalized indexes - for k, v in clsdict.items(): - base_dict[k] = v - original_dict = base_dict - if size is not undefined and '_size_' in original_dict: - raise TypeError('_size_ cannot be set if "size" is passed in header') - add_order = isinstance(clsdict, _NamedTupleDict) - clsdict = _NamedTupleDict() - clsdict.setdefault('_size_', size or TupleSize.fixed) - unnumbered = OrderedDict() - numbered = OrderedDict() - _order_ = original_dict.pop('_order_', []) - if _order_ : - _order_ = _order_.replace(',',' ').split() - add_order = False - # and process this class - for k, v in original_dict.items(): - if k not in original_dict._field_names: - clsdict[k] = v - else: - # TODO:normalize v here - if isinstance(v, baseinteger): - # assume an offset - v = v, undefined, undefined - i = v[0] + 1 - target = numbered - elif isinstance(v, basestring): - # assume a docstring - if add_order: - v = i, v, undefined - i += 1 - target = numbered - else: - v = undefined, v, undefined - target = unnumbered - elif isinstance(v, tuple) and len(v) in (2, 3) and isinstance(v[0], baseinteger) and isinstance(v[1], (basestring, NoneType)): - # assume an offset, a docstring, and (maybe) a default - if len(v) == 2: - v = v + (undefined, ) - v = v - i = v[0] + 1 - target = numbered - elif isinstance(v, tuple) and len(v) in (1, 2) and isinstance(v[0], (basestring, NoneType)): - # assume a docstring, and (maybe) a default - if len(v) == 1: - v = v + (undefined, ) - if add_order: - v = (i, ) + v - i += 1 - target = numbered - else: - v = (undefined, ) + v - target = unnumbered - else: - # refuse to guess further - raise ValueError('not sure what to do with %s=%r (should be OFFSET [, DOC [, DEFAULT]])' % (k, v)) - target[k] = v - # all index values have been normalized - # deal with _order_ (or lack thereof) - fields = [] - aliases = [] - seen = set() - max_len = 0 - if not _order_: - if unnumbered: - raise ValueError("_order_ not specified and OFFSETs not declared for %r" % (unnumbered.keys(), )) - for name, (index, doc, default) in sorted(numbered.items(), key=lambda nv: (nv[1][0], nv[0])): - if index in seen: - aliases.append(name) - else: - fields.append(name) - seen.add(index) - max_len = max(max_len, index + 1) - offsets = numbered - else: - # check if any unnumbered not in _order_ - missing = set(unnumbered) - set(_order_) - if missing: - raise ValueError("unable to order fields: %s (use _order_ or specify OFFSET" % missing) - offsets = OrderedDict() - # if any unnumbered, number them from their position in _order_ - i = 0 - for k in _order_: - try: - index, doc, default = unnumbered.pop(k, None) or numbered.pop(k) - except IndexError: - raise ValueError('%s (from _order_) not found in %s' % (k, cls)) - if index is not undefined: - i = index - if i in seen: - aliases.append(k) - else: - fields.append(k) - seen.add(i) - offsets[k] = i, doc, default - i += 1 - max_len = max(max_len, i) - # now handle anything in numbered - for k, (index, doc, default) in sorted(numbered.items(), key=lambda nv: (nv[1][0], nv[0])): - if index in seen: - aliases.append(k) - else: - fields.append(k) - seen.add(index) - offsets[k] = index, doc, default - max_len = max(max_len, index+1) - - # at this point fields and aliases should be ordered lists, offsets should be an - # OrdededDict with each value an int, str or None or undefined, default or None or undefined - assert len(fields) + len(aliases) == len(offsets), "number of fields + aliases != number of offsets" - assert set(fields) & set(offsets) == set(fields), "some fields are not in offsets: %s" % set(fields) & set(offsets) - assert set(aliases) & set(offsets) == set(aliases), "some aliases are not in offsets: %s" % set(aliases) & set(offsets) - for name, (index, doc, default) in offsets.items(): - assert isinstance(index, baseinteger), "index for %s is not an int (%s:%r)" % (name, type(index), index) - assert isinstance(doc, (basestring, NoneType)) or doc is undefined, "doc is not a str, None, nor undefined (%s:%r)" % (name, type(doc), doc) - - # create descriptors for fields - for name, (index, doc, default) in offsets.items(): - clsdict[name] = _TupleAttributeAtIndex(name, index, doc, default) - clsdict['__slots__'] = () - - # create our new NamedTuple type - namedtuple_class = super(NamedTupleMeta, metacls).__new__(metacls, cls, bases, clsdict) - namedtuple_class._fields_ = fields - namedtuple_class._aliases_ = aliases - namedtuple_class._defined_len_ = max_len - return namedtuple_class - - @staticmethod - def _convert_fields(*namedtuples): - "create list of index, doc, default triplets for cls in namedtuples" - all_fields = [] - for cls in namedtuples: - base = len(all_fields) - for field in cls._fields_: - desc = getattr(cls, field) - all_fields.append((field, base+desc.index, desc.__doc__, desc.default)) - return all_fields - - def __add__(cls, other): - "A new NamedTuple is created by concatenating the _fields_ and adjusting the descriptors" - if not isinstance(other, NamedTupleMeta): - return NotImplemented - return NamedTupleMeta('%s%s' % (cls.__name__, other.__name__), (cls, other), {}) - - def __call__(cls, *args, **kwds): - """Creates a new NamedTuple class or an instance of a NamedTuple subclass. - - NamedTuple should have args of (class_name, names, module) - - `names` can be: - - * A string containing member names, separated either with spaces or - commas. Values are auto-numbered from 1. - * An iterable of member names. Values are auto-numbered from 1. - * An iterable of (member name, value) pairs. - * A mapping of member name -> value. - - `module`, if set, will be stored in the new class' __module__ attribute; - - Note: if `module` is not set this routine will attempt to discover the - calling module by walking the frame stack; if this is unsuccessful - the resulting class will not be pickleable. - - subclass should have whatever arguments and/or keywords will be used to create an - instance of the subclass - """ - if cls is NamedTuple: - original_args = args - original_kwds = kwds.copy() - # create a new subclass - try: - if 'class_name' in kwds: - class_name = kwds.pop('class_name') - else: - class_name, args = args[0], args[1:] - if 'names' in kwds: - names = kwds.pop('names') - else: - names, args = args[0], args[1:] - if 'module' in kwds: - module = kwds.pop('module') - elif args: - module, args = args[0], args[1:] - else: - module = None - if 'type' in kwds: - type = kwds.pop('type') - elif args: - type, args = args[0], args[1:] - else: - type = None - - except IndexError: - raise TypeError('too few arguments to NamedTuple: %s, %s' % (original_args, original_kwds)) - if args or kwds: - raise TypeError('too many arguments to NamedTuple: %s, %s' % (original_args, original_kwds)) - if PY2: - # if class_name is unicode, attempt a conversion to ASCII - if isinstance(class_name, unicode): - try: - class_name = class_name.encode('ascii') - except UnicodeEncodeError: - raise TypeError('%r is not representable in ASCII' % (class_name, )) - # quick exit if names is a NamedTuple - if isinstance(names, NamedTupleMeta): - names.__name__ = class_name - if type is not None and type not in names.__bases__: - names.__bases__ = (type, ) + names.__bases__ - return names - - metacls = cls.__class__ - bases = (cls, ) - clsdict = metacls.__prepare__(class_name, bases) - - # special processing needed for names? - if isinstance(names, basestring): - names = names.replace(',', ' ').split() - if isinstance(names, (tuple, list)) and isinstance(names[0], basestring): - names = [(e, i) for (i, e) in enumerate(names)] - # Here, names is either an iterable of (name, index) or (name, index, doc, default) or a mapping. - item = None # in case names is empty - for item in names: - if isinstance(item, basestring): - # mapping - field_name, field_index = item, names[item] - else: - # non-mapping - if len(item) == 2: - field_name, field_index = item - else: - field_name, field_index = item[0], item[1:] - clsdict[field_name] = field_index - if type is not None: - if not isinstance(type, tuple): - type = (type, ) - bases = type + bases - namedtuple_class = metacls.__new__(metacls, class_name, bases, clsdict) - - # TODO: replace the frame hack if a blessed way to know the calling - # module is ever developed - if module is None: - try: - module = _sys._getframe(1).f_globals['__name__'] - except (AttributeError, ValueError, KeyError): - pass - if module is None: - _make_class_unpicklable(namedtuple_class) - else: - namedtuple_class.__module__ = module - - return namedtuple_class - else: - # instantiate a subclass - namedtuple_instance = cls.__new__(cls, *args, **kwds) - if isinstance(namedtuple_instance, cls): - namedtuple_instance.__init__(*args, **kwds) - return namedtuple_instance - - @_bltin_property - def __fields__(cls): - return list(cls._fields_) - # collections.namedtuple compatibility - _fields = __fields__ - - @_bltin_property - def __aliases__(cls): - return list(cls._aliases_) - - def __repr__(cls): - return "" % (cls.__name__, ) - -namedtuple_dict = _Addendum( - dict=NamedTupleMeta.__prepare__('NamedTuple', (object, )), - doc="NamedTuple base class.\n\n Derive from this class to define new NamedTuples.\n\n", - ns=globals(), - ) - -@namedtuple_dict -def __new__(cls, *args, **kwds): - if cls._size_ is TupleSize.fixed and len(args) > cls._defined_len_: - raise TypeError('%d fields expected, %d received' % (cls._defined_len_, len(args))) - unknown = set(kwds) - set(cls._fields_) - set(cls._aliases_) - if unknown: - raise TypeError('unknown fields: %r' % (unknown, )) - final_args = list(args) + [undefined] * (len(cls.__fields__) - len(args)) - for field, value in kwds.items(): - index = getattr(cls, field).index - if final_args[index] != undefined: - raise TypeError('field %s specified more than once' % field) - final_args[index] = value - missing = [] - for index, value in enumerate(final_args): - if value is undefined: - # look for default values - name = cls.__fields__[index] - default = getattr(cls, name).default - if default is undefined: - missing.append(name) - else: - final_args[index] = default - if missing: - if cls._size_ in (TupleSize.fixed, TupleSize.minimum): - raise TypeError('values not provided for field(s): %s' % ', '.join(missing)) - while final_args and final_args[-1] is undefined: - final_args.pop() - missing.pop() - if cls._size_ is not TupleSize.variable or undefined in final_args: - raise TypeError('values not provided for field(s): %s' % ', '.join(missing)) - return tuple.__new__(cls, tuple(final_args)) - -@namedtuple_dict -def __reduce_ex__(self, proto): - return self.__class__, tuple(getattr(self, f) for f in self._fields_) - -@namedtuple_dict -def __repr__(self): - if len(self) == len(self._fields_): - return "%s(%s)" % ( - self.__class__.__name__, ', '.join(['%s=%r' % (f, o) for f, o in zip(self._fields_, self)]) - ) - else: - return '%s(%s)' % (self.__class__.__name__, ', '.join([repr(o) for o in self])) - -@namedtuple_dict -def __str__(self): - return "%s(%s)" % ( - self.__class__.__name__, ', '.join(['%r' % (getattr(self, f), ) for f in self._fields_]) - ) - -@namedtuple_dict -@_bltin_property -def _fields_(self): - return list(self.__class__._fields_) - - # compatibility methods with stdlib namedtuple -@namedtuple_dict -@_bltin_property -def __aliases__(self): - return list(self.__class__._aliases_) - -@namedtuple_dict -@_bltin_property -def _fields(self): - return list(self.__class__._fields_) - -@namedtuple_dict -@classmethod -def _make(cls, iterable, new=None, len=None): - return cls.__new__(cls, *iterable) - -@namedtuple_dict -def _asdict(self): - return OrderedDict(zip(self._fields_, self)) - -@namedtuple_dict -def _replace(self, **kwds): - current = self._asdict() - current.update(kwds) - return self.__class__(**current) - -NamedTuple = NamedTupleMeta('NamedTuple', (object, ), namedtuple_dict.resolve()) -del namedtuple_dict - - -# Enum - - # _init_ and value and AddValue - # ----------------------------- - # by default, when defining a member everything after the = is "the value", everything is - # passed to __new__, everything is passed to __init__ - # - # if _init_ is present then - # if `value` is not in _init_, everything is "the value", defaults apply - # if `value` is in _init_, only the first thing after the = is the value, and the rest will - # be passed to __init__ - # if fewer values are present for member assignment than _init_ calls for, _generate_next_value_ - # will be called in an attempt to generate them - # - # if AddValue is present then - # _generate_next_value_ is always called, and any generated values are prepended to provided - # values (custom _gnv_s can change that) - # default _init_ rules apply - - - # Constants used in Enum - -@export(globals()) -class EnumConstants(NamedConstant): - AddValue = constant('addvalue', 'prepends value(s) from _generate_next_value_ to each member') - MagicValue = constant('magicvalue', 'calls _generate_next_value_ when no arguments are given') - MultiValue = constant('multivalue', 'each member can have several values') - NoAlias = constant('noalias', 'duplicate valued members are distinct, not aliased') - Unique = constant('unique', 'duplicate valued members are not allowed') - def __repr__(self): - return self._name_ - - - # Dummy value for Enum as EnumType explicity checks for it, but of course until - # EnumType finishes running the first time the Enum class doesn't exist. This - # is also why there are checks in EnumType like `if Enum is not None`. - # - # Ditto for Flag. - -Enum = ReprEnum = IntEnum = StrEnum = Flag = IntFlag = EJECT = KEEP = None - -class enum(object): - """ - Helper class to track args, kwds. - """ - def __init__(self, *args, **kwds): - self._args = args - self._kwds = dict(kwds.items()) - self._hash = hash(args) - self.name = None - - @_bltin_property - def args(self): - return self._args - - @_bltin_property - def kwds(self): - return self._kwds.copy() - - def __hash__(self): - return self._hash - - def __eq__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return self.args == other.args and self.kwds == other.kwds - - def __ne__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return self.args != other.args or self.kwds != other.kwds - - def __repr__(self): - final = [] - args = ', '.join(['%r' % (a, ) for a in self.args]) - if args: - final.append(args) - kwds = ', '.join([('%s=%r') % (k, v) for k, v in enumsort(list(self.kwds.items()))]) - if kwds: - final.append(kwds) - return '%s(%s)' % (self.__class__.__name__, ', '.join(final)) - -_auto_null = SentinelType('no_value', (object, ), {}) -class auto(enum): - """ - Instances are replaced with an appropriate value in Enum class suites. - """ - enum_member = _auto_null - _value = _auto_null - _operations = [] - - def __and__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_and_, (self, other))) - return new_auto - - def __rand__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_and_, (other, self))) - return new_auto - - def __invert__(self): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_inv_, (self,))) - return new_auto - - def __or__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_or_, (self, other))) - return new_auto - - def __ror__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_or_, (other, self))) - return new_auto - - def __xor__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_xor_, (self, other))) - return new_auto - - def __rxor__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_xor_, (other, self))) - return new_auto - - def __abs__(self): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_abs_, (self, ))) - return new_auto - - def __add__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_add_, (self, other))) - return new_auto - - def __radd__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_add_, (other, self))) - return new_auto - - def __neg__(self): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_neg_, (self, ))) - return new_auto - - def __pos__(self): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_pos_, (self, ))) - return new_auto - - if PY2: - def __div__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_div_, (self, other))) - return new_auto - - def __rdiv__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_div_, (other, self))) - return new_auto - - def __floordiv__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_floordiv_, (self, other))) - return new_auto - - def __rfloordiv__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_floordiv_, (other, self))) - return new_auto - - def __truediv__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_truediv_, (self, other))) - return new_auto - - def __rtruediv__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_truediv_, (other, self))) - return new_auto - - def __lshift__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_lshift_, (self, other))) - return new_auto - - def __rlshift__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_lshift_, (other, self))) - return new_auto - - def __rshift__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_rshift_, (self, other))) - return new_auto - - def __rrshift__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_rshift_, (other, self))) - return new_auto - - def __mod__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_mod_, (self, other))) - return new_auto - - def __rmod__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_mod_, (other, self))) - return new_auto - - def __mul__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_mul_, (self, other))) - return new_auto - - def __rmul__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_mul_, (other, self))) - return new_auto - - def __pow__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_pow_, (self, other))) - return new_auto - - def __rpow__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_pow_, (other, self))) - return new_auto - - def __sub__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_sub_, (self, other))) - return new_auto - - def __rsub__(self, other): - new_auto = self.__class__() - new_auto._operations = self._operations[:] - new_auto._operations.append((_sub_, (other, self))) - return new_auto - - def __repr__(self): - if self._operations: - return 'auto(...)' - else: - return 'auto(%r, *%r, **%r)' % (self._value, self._args, self._kwds) - - @_bltin_property - def value(self): - if self._value is not _auto_null and self._operations: - raise TypeError('auto() object out of sync') - elif self._value is _auto_null and not self._operations: - return self._value - elif self._value is not _auto_null: - return self._value - else: - return self._resolve() - - @value.setter - def value(self, value): - if self._operations: - value = self._resolve(value) - self._value = value - - def _resolve(self, base_value=None): - cls = self.__class__ - for op, params in self._operations: - values = [] - for param in params: - if isinstance(param, cls): - if param.value is _auto_null: - if base_value is None: - return _auto_null - else: - values.append(base_value) - else: - values.append(param.value) - else: - values.append(param) - value = op(*values) - self._operations[:] = [] - self._value = value - return value - - -class _EnumArgSpec(NamedTuple): - args = 0, 'all args except *args and **kwds' - varargs = 1, 'the name of the *args variable' - keywords = 2, 'the name of the **kwds variable' - defaults = 3, 'any default values' - required = 4, 'number of required values (no default available)' - - def __new__(cls, _new_func): - argspec = getargspec(_new_func) - args, varargs, keywords, defaults = argspec - if defaults: - reqs = args[1:-len(defaults)] - else: - reqs = args[1:] - return tuple.__new__(_EnumArgSpec, (args, varargs, keywords, defaults, reqs)) - - -class _proto_member: - """ - intermediate step for enum members between class execution and final creation - """ - - def __init__(self, value): - self.value = value - - def __set_name__(self, enum_class, member_name): - """ - convert each quasi-member into an instance of the new enum class - """ - # first step: remove ourself from enum_class - delattr(enum_class, member_name) - # second step: create member based on enum_class - value = self.value - kwds = {} - args = () - init_args = () - extra_mv_args = () - multivalue = None - if isinstance(value, tuple) and value and isinstance(value[0], auto): - multivalue = value - value = value[0] - if isinstance(value, auto) and value.value is _auto_null: - args = value.args - kwds = value.kwds - elif isinstance(value, auto): - kwds = value.kwds - args = (value.value, ) + value.args - value = value.value - elif isinstance(value, enum): - args = value.args - kwds = value.kwds - elif isinstance(value, Member): - value = value.value - args = (value, ) - elif not isinstance(value, tuple): - args = (value, ) - else: - args = value - if multivalue is not None: - value = (value, ) + multivalue[1:] - kwds = {} - args = value - del multivalue - # possibilities - # - # - no init, multivalue -> __new__[0], __init__(*[:]), extra=[1:] - # - init w/o value, multivalue -> __new__[0], __init__(*[:]), extra=[1:] - # - # - init w/value, multivalue -> __new__[0], __init__(*[1:]), extra=[1:] - # - # - init w/value, no multivalue -> __new__[0], __init__(*[1:]), extra=[] - # - # - init w/o value, no multivalue -> __new__[:], __init__(*[:]), extra=[] - # - no init, no multivalue -> __new__[:], __init__(*[:]), extra=[] - if enum_class._multivalue_ or 'value' in enum_class._creating_init_: - if enum_class._multivalue_: - # when multivalue is True, creating_init can be anything - mv_arg = args[0] - extra_mv_args = args[1:] - if 'value' in enum_class._creating_init_: - init_args = args[1:] - else: - init_args = args - args = args[0:1] - value = args[0] - else: - # 'value' is definitely in creating_init - if enum_class._auto_init_ and enum_class._new_args_: - # we have a custom __new__ and an auto __init__ - # divvy up according to number of params in each - init_args = args[-len(enum_class._creating_init_)+1:] - if not enum_class._auto_args_: - args = args[:len(enum_class._new_args_.args)] - value = args[0] - elif enum_class._auto_init_: - # don't pass in value - init_args = args[1:] - args = args[0:1] - value = args[0] - elif enum_class._new_args_: - # do not modify args - value = args[0] - else: - # keep all args for user-defined __init__ - # keep value as-is - init_args = args - else: - # either no creating_init, or it doesn't have 'value' - init_args = args - if enum_class._member_type_ is tuple: # special case for tuple enums - args = (args, ) # wrap it one more time - if not enum_class._use_args_: - enum_member = enum_class._new_member_(enum_class) - if not hasattr(enum_member, '_value_'): - enum_member._value_ = value - else: - enum_member = enum_class._new_member_(enum_class, *args, **kwds) - if not hasattr(enum_member, '_value_'): - if enum_class._member_type_ is object: - enum_member._value_ = value - else: - try: - enum_member._value_ = enum_class._member_type_(*args, **kwds) - except Exception: - te = TypeError('_value_ not set in __new__, unable to create it') - te.__cause__ = None - raise te - value = enum_member._value_ - enum_member._name_ = member_name - enum_member.__objclass__ = enum_class - enum_member.__init__(*init_args, **kwds) - enum_member._sort_order_ = len(enum_class._member_names_) - # If another member with the same value was already defined, the - # new member becomes an alias to the existing one. - if enum_class._noalias_: - # unless NoAlias was specified - enum_class._member_names_.append(member_name) - else: - nonunique = defaultdict(list) - try: - try: - # try to do a fast lookup to avoid the quadratic loop - enum_member = enum_class._value2member_map_[value] - if enum_class._unique_: - nonunique[enum_member.name].append(member_name) - except TypeError: - # unhashable members are stored elsewhere - for unhashable_value, canonical_member in enum_class._value2member_seq_: - name = canonical_member.name - if unhashable_value == enum_member._value_: - if enum_class._unique_: - nonunique[name].append(member_name) - enum_member = canonical_member - break - else: - raise KeyError - except KeyError: - # this could still be an alias if the value is multi-bit and the - # class is a flag class - if ( - Flag is None - or not issubclass(enum_class, Flag) - ): - # no other instances found, record this member in _member_names_ - enum_class._member_names_.append(member_name) - elif ( - Flag is not None - and issubclass(enum_class, Flag) - and _is_single_bit(value) - ): - # no other instances found, record this member in _member_names_ - enum_class._member_names_.append(member_name) - if nonunique: - # duplicates not allowed if Unique specified - message = [] - for name, aliases in nonunique.items(): - bad_aliases = ','.join(aliases) - message.append('%s --> %s [%r]' % (name, bad_aliases, enum_class[name].value)) - raise ValueError( - '%s: duplicate names found: %s' % - (enum_class.__name__, '; '.join(message)) - ) - # if self.value is an `auto()`, replace the value attribute with the new enum member - if isinstance(self.value, auto): - self.value.enum_member = enum_member - # get redirect in place before adding to _member_map_ - # but check for other instances in parent classes first - need_override = False - descriptor = None - descriptor_property = None - for base in enum_class.__mro__[1:]: - descriptor = base.__dict__.get(member_name) - if descriptor is not None: - if isinstance(descriptor, (property, DynamicClassAttribute)): - break - else: - need_override = True - if isinstance(descriptor, _bltin_property) and descriptor_property is None: - descriptor_property = descriptor - # keep looking for an enum.property - descriptor = descriptor or descriptor_property - if descriptor and not need_override: - # previous enum.property found, no further action needed - pass - else: - redirect = property() - redirect.__set_name__(enum_class, member_name) - if descriptor and need_override: - # previous enum.property found, but some other inherited - # attribute is in the way; copy fget, fset, fdel to this one - redirect.fget = descriptor.fget - redirect.fset = descriptor.fset - redirect.fdel = descriptor.fdel - setattr(enum_class, member_name, redirect) - # now add to _member_map_ (even aliases) - enum_class._member_map_[member_name] = enum_member - # - # process (possible) MultiValues - values = (value, ) + extra_mv_args - if enum_class._multivalue_ and mv_arg not in values: - values += (mv_arg, ) - enum_member._values_ = values - for value in values: - # first check if value has already been used - if enum_class._multivalue_ and ( - value in enum_class._value2member_map_ - or any(v == value for (v, m) in enum_class._value2member_seq_) - ): - raise ValueError('%r has already been used' % (value, )) - try: - # This may fail if value is not hashable. We can't add the value - # to the map, and by-value lookups for this value will be - # linear. - if enum_class._noalias_: - raise TypeError('cannot use dict to store value') - enum_class._value2member_map_[value] = enum_member - except TypeError: - enum_class._value2member_seq_ += ((value, enum_member), ) - - -class _EnumDict(dict): - """Track enum member order and ensure member names are not reused. - - EnumType will use the names found in self._member_names as the - enumeration member names. - """ - def __init__(self, cls_name, settings, start, constructor_init, constructor_start, constructor_boundary): - super(_EnumDict, self).__init__() - self._cls_name = cls_name - self._constructor_init = constructor_init - self._constructor_start = constructor_start - self._constructor_boundary = constructor_boundary - self._generate_next_value = None - self._member_names = [] - self._member_names_set = set() - self._settings = settings - self._addvalue = addvalue = AddValue in settings - self._magicvalue = MagicValue in settings - self._multivalue = MultiValue in settings - if self._addvalue and self._magicvalue: - raise TypeError('%r: AddValue and MagicValue are mutually exclusive' % cls_name) - if self._multivalue and self._magicvalue: - raise TypeError('%r: MultiValue and MagicValue are mutually exclusive' % cls_name) - self._start = start - self._addvalue_value = start - self._new_args = () - self._auto_args = False - # when the magic turns off - self._locked = MagicValue not in settings - # if init fields are specified - self._init = [] - # list of temporary names - self._ignore = [] - if self._magicvalue: - self._ignore = ['property', 'staticmethod', 'classmethod'] - self._ignore_init_done = False - # if _sunder_ values can be changed via the class body - self._allow_init = True - self._last_values = [] - - def __getitem__(self, key): - if key == self._cls_name and self._cls_name not in self: - return enum - elif ( - self._locked - or key in self - or key in self._ignore - or _is_sunder(key) - or _is_dunder(key) - ): - return super(_EnumDict, self).__getitem__(key) - elif self._magicvalue: - value = self._generate_next_value(key, self._start, len(self._member_names), self._last_values[:]) - self.__setitem__(key, value) - return value - else: - raise Exception('Magic is not set -- why am I here?') - - def __setitem__(self, key, value): - """Changes anything not sundured, dundered, nor a descriptor. - - If an enum member name is used twice, an error is raised; duplicate - values are not checked for. - - Single underscore (sunder) names are reserved. - """ - # Flag classes that have MagicValue and __new__ will get a generated _gnv_ - if _is_internal_class(self._cls_name, value): - pass - elif _is_private_name(self._cls_name, key): - pass - elif _is_sunder(key): - if key not in ( - '_init_', '_settings_', '_order_', '_ignore_', '_start_', - '_create_pseudo_member_', '_create_pseudo_member_values_', - '_generate_next_value_', '_boundary_', '_numeric_repr_', - '_missing_', '_missing_value_', '_missing_name_', - '_iter_member_', '_iter_member_by_value_', '_iter_member_by_def_', - ): - raise ValueError('%r: _sunder_ names, such as %r, are reserved for future Enum use' - % (self._cls_name, key) - ) - elif not self._allow_init and key not in ( - 'create_pseudo_member_', '_missing_', '_missing_value_', '_missing_name_', - ): - # sunder is used during creation, must be specified first - raise ValueError('%r: cannot set %r after init phase' % (self._cls_name, key)) - elif key == '_ignore_': - if self._ignore_init_done: - raise TypeError('%r: ignore can only be specified once' % self._cls_name) - if isinstance(value, basestring): - value = value.split() - else: - value = list(value) - self._ignore = value - already = set(value) & self._member_names_set - if already: - raise ValueError('%r: _ignore_ cannot specify already set names %s' % ( - self._cls_name, - ', '.join(repr(a) for a in already) - )) - self._ignore_init_done = True - elif key == '_boundary_': - if self._constructor_boundary: - raise TypeError('%r: boundary specified in constructor and class body' % self._cls_name) - elif key == '_start_': - if self._constructor_start: - raise TypeError('%r: start specified in constructor and class body' % self._cls_name) - self._start = value - elif key == '_settings_': - if not isinstance(value, (set, tuple)): - value = (value, ) - if not isinstance(value, set): - value = set(value) - self._settings |= value - if NoAlias in value and Unique in value: - raise TypeError('%r: NoAlias and Unique are mutually exclusive' % self._cls_name) - elif MultiValue in value and NoAlias in value: - raise TypeError('cannot specify both MultiValue and NoAlias' % self._cls_name) - allowed_settings = dict.fromkeys(['addvalue', 'magicvalue', 'noalias', 'unique', 'multivalue']) - for arg in self._settings: - if arg not in allowed_settings: - raise TypeError('%r: unknown qualifier %r (from %r)' % (self._cls_name, arg, value)) - allowed_settings[arg] = True - self._multivalue = allowed_settings['multivalue'] - self._addvalue = allowed_settings['addvalue'] - self._magicvalue = allowed_settings['magicvalue'] - self._locked = not self._magicvalue - if self._magicvalue and not self._ignore_init_done: - self._ignore = ['property', 'classmethod', 'staticmethod'] - if self._addvalue and self._init and 'value' not in self._init: - self._init.insert(0, 'value') - value = tuple(self._settings) - elif key == '_init_': - if self._constructor_init: - raise TypeError('%r: init specified in constructor and in class body' % self._cls_name) - _init_ = value - if isinstance(_init_, basestring): - _init_ = _init_.replace(',',' ').split() - if self._addvalue and 'value' not in self._init: - self._init.insert(0, 'value') - if self._magicvalue: - raise TypeError("%r: _init_ and MagicValue are mutually exclusive" % self._cls_name) - self._init = _init_ - value = _init_ - elif key == '_generate_next_value_': - gnv = value - if value is not None: - if isinstance(value, staticmethod): - gnv = value.__func__ - elif isinstance(value, classmethod): - raise TypeError('%r: _generate_next_value must be a staticmethod, not a classmethod' % self._cls_name) - else: - gnv = value - value = staticmethod(value) - self._auto_args = _check_auto_args(value) - setattr(self, '_generate_next_value', gnv) - elif _is_dunder(key): - if key == '__order__': - key = '_order_' - if not self._allow_init: - # _order_ is used during creation, must be specified first - raise ValueError('%r: cannot set %r after init phase' % (self._cls_name, key)) - elif key == '__new__': # and self._new_to_init: - if isinstance(value, staticmethod): - value = value.__func__ - self._new_args = _EnumArgSpec(value) - elif key == '__init_subclass__': - if not isinstance(value, classmethod): - value = classmethod(value) - if _is_descriptor(value): - self._locked = True - elif key in self._member_names_set: - # descriptor overwriting an enum? - raise TypeError('%r: attempt to reuse name: %r' % (self._cls_name, key)) - elif key in self._ignore: - pass - elif not _is_descriptor(value): - self._allow_init = False - if key in self: - # enum overwriting a descriptor? - raise TypeError('%r: %s already defined as %r' % (self._cls_name, key, self[key])) - if type(value) is enum: - value.name = key - if self._addvalue: - raise TypeError('%r: enum() and AddValue are incompatible' % self._cls_name) - elif self._addvalue and not self._multivalue: - # generate a value - value = self._gnv(key, value) - elif self._multivalue: - # make sure it's a tuple - if not isinstance(value, tuple): - value = (value, ) - if isinstance(value[0], auto): - value = (self._convert_auto(key, value[0]), ) + value[1:] - if self._addvalue: - value = self._gnv(key, value) - elif isinstance(value, auto): - value = self._convert_auto(key, value) - elif isinstance(value, tuple) and value and isinstance(value[0], auto): - value = (self._convert_auto(key, value[0]), ) + value[1:] - elif not isinstance(value, auto): - # call generate maybe if - # - init is specified; or - # - __new__ is specified; - # and either of them call for more values than are present - new_args = () or self._new_args and self._new_args.required - target_len = len(self._init or new_args) - if isinstance(value, tuple): - source_len = len(value) - else: - source_len = 1 - multi_args = len(self._init) > 1 or new_args - if source_len < target_len : - value = self._gnv(key, value) - else: - pass - if self._init: - if isinstance(value, auto): - test_value = value.args - elif not isinstance(value, tuple): - test_value = (value, ) - else: - test_value = value - if len(self._init) != len(test_value): - raise TypeError( - '%s.%s: number of fields provided do not match init [%r != %r]' - % (self._cls_name, key, self._init, test_value) - ) - self._member_names.append(key) - self._member_names_set.add(key) - else: - # not a new member, turn off the autoassign magic - self._locked = True - self._allow_init = False - if not (_is_sunder(key) or _is_dunder(key) or _is_private_name(self._cls_name, key) or _is_descriptor(value)): - if isinstance(value, auto): - self._last_values.append(value.value) - elif isinstance(value, tuple) and value and isinstance(value[0], auto): - self._last_values.append(value[0].value) - elif isinstance(value, tuple): - if value: - self._last_values.append(value[0]) - else: - self._last_values.append(value) - super(_EnumDict, self).__setitem__(key, value) - - def _convert_auto(self, key, value): - # if auto.args or auto.kwds, compare to _init_ and __new__ -- if lacking, call gnv - # if not auto.args|kwds but auto.value is _auto_null -- call gnv - if value.args or value.kwds or value.value is _auto_null: - if value.args or value.kwds: - values = value.args - else: - values = () - new_args = () or self._new_args and self._new_args.required - target_len = len(self._init or new_args) or 1 - if isinstance(values, tuple): - source_len = len(values) - else: - source_len = 1 - multi_args = len(self._init) > 1 or new_args - if source_len < target_len : - values = self._gnv(key, values) - if value.args: - value._args = values - else: - value.value = values - return value - - def _gnv(self, key, value): - # generate a value - if self._auto_args: - if not isinstance(value, tuple): - value = (value, ) - value = self._generate_next_value(key, self._start, len(self._member_names), self._last_values[:], *value) - else: - value = self._generate_next_value(key, self._start, len(self._member_names), self._last_values[:]) - if isinstance(value, tuple) and len(value) == 1: - value = value[0] - return value - - -no_arg = SentinelType('no_arg', (object, ), {}) -class EnumType(type): - """Metaclass for Enum""" - - @classmethod - def __prepare__(metacls, cls, bases, init=None, start=None, settings=(), boundary=None, **kwds): - metacls._check_for_existing_members_(cls, bases) - if Flag is None and cls == 'Flag': - initial_flag = True - else: - initial_flag = False - # settings are a combination of current and all past settings - constructor_init = init is not None - constructor_start = start is not None - constructor_boundary = boundary is not None - if not isinstance(settings, tuple): - settings = settings, - settings = set(settings) - generate = None - order = None - # inherit previous flags - member_type, first_enum = metacls._get_mixins_(cls, bases) - if first_enum is not None: - generate = getattr(first_enum, '_generate_next_value_', None) - generate = getattr(generate, 'im_func', generate) - settings |= metacls._get_settings_(bases) - init = init or first_enum._auto_init_[:] - order = first_enum._order_function_ - if start is None: - start = first_enum._start_ - else: - # first time through -- creating Enum itself - start = 1 - # check for custom settings - if AddValue in settings and init and 'value' not in init: - if isinstance(init, list): - init.insert(0, 'value') - else: - init = 'value ' + init - if NoAlias in settings and Unique in settings: - raise TypeError('%r: NoAlias and Unique are mutually exclusive' % cls) - if MultiValue in settings and NoAlias in settings: - raise TypeError('%r: MultiValue and NoAlias are mutually exclusive' % cls) - allowed_settings = dict.fromkeys(['addvalue', 'magicvalue', 'noalias', 'unique', 'multivalue']) - for arg in settings: - if arg not in allowed_settings: - raise TypeError('%r: unknown qualifier %r' % (cls, arg)) - enum_dict = _EnumDict(cls_name=cls, settings=settings, start=start, constructor_init=constructor_init, constructor_start=constructor_start, constructor_boundary=constructor_boundary) - enum_dict._member_type = member_type - enum_dict._base_type = ('enum', 'flag')[ - Flag is None and cls == 'Flag' - or - Flag is not None and any(issubclass(b, Flag) for b in bases) - ] - if Flag is not None and any(b is Flag for b in bases) and member_type not in (baseinteger + (object, )): - if Flag in bases: - # when a non-int data type is mixed in with Flag, we end up - # needing two values for two `__new__`s: - # - the integer value for the Flag itself; and - # - the mix-in value for the mix-in - # - # we provide a default `_generate_next_value_` to supply the int - # argument, and a default `__new__` to keep the two straight - def _generate_next_value_(name, start, count, values, *args, **kwds): - return (2 ** count, ) + args - enum_dict['_generate_next_value_'] = staticmethod(_generate_next_value_) - def __new__(cls, flag_value, type_value): - obj = member_type.__new__(cls, type_value) - obj._value_ = flag_value - return obj - enum_dict['__new__'] = __new__ - else: - try: - enum_dict._new_args = _EnumArgSpec(first_enum.__new_member__) - except TypeError: - pass - elif not initial_flag: - if hasattr(first_enum, '__new_member__'): - enum_dict._new_args = _EnumArgSpec(first_enum.__new_member__) - if generate: - enum_dict['_generate_next_value_'] = generate - enum_dict._inherited_gnv = True - if init is not None: - if isinstance(init, basestring): - init = init.replace(',',' ').split() - enum_dict._init = init - elif hasattr(first_enum, '__new_member__'): - enum_dict._new_args = _EnumArgSpec(first_enum.__new_member__) - if order is not None: - enum_dict['_order_'] = staticmethod(order) - return enum_dict - - def __init__(cls, *args , **kwds): - pass - - def __new__(metacls, cls, bases, clsdict, init=None, start=None, settings=(), boundary=None, **kwds): - # handle py2 case first - if type(clsdict) is not _EnumDict: - # py2 and/or functional API gyrations - init = clsdict.pop('_init_', None) - start = clsdict.pop('_start_', None) - settings = clsdict.pop('_settings_', ()) - _order_ = clsdict.pop('_order_', clsdict.pop('__order__', None)) - _ignore_ = clsdict.pop('_ignore_', None) - _create_pseudo_member_ = clsdict.pop('_create_pseudo_member_', None) - _create_pseudo_member_values_ = clsdict.pop('_create_pseudo_member_values_', None) - _generate_next_value_ = clsdict.pop('_generate_next_value_', None) - _missing_ = clsdict.pop('_missing_', None) - _missing_value_ = clsdict.pop('_missing_value_', None) - _missing_name_ = clsdict.pop('_missing_name_', None) - _boundary_ = clsdict.pop('_boundary_', None) - _iter_member_ = clsdict.pop('_iter_member_', None) - _iter_member_by_value_ = clsdict.pop('_iter_member_by_value_', None) - _iter_member_by_def_ = clsdict.pop('_iter_member_by_def_', None) - __new__ = clsdict.pop('__new__', None) - __new__ = getattr(__new__, 'im_func', __new__) - __new__ = getattr(__new__, '__func__', __new__) - enum_members = dict([ - (k, v) for (k, v) in clsdict.items() - if not (_is_sunder(k) or _is_dunder(k) or _is_private_name(cls, k) or _is_descriptor(v)) - ]) - original_dict = clsdict - clsdict = metacls.__prepare__(cls, bases, init=init, start=start) - if settings: - clsdict['_settings_'] = settings - init = init or clsdict._init - if _order_ is None: - _order_ = clsdict.get('_order_') - if _order_ is not None: - _order_ = _order_.__get__(cls) - if isinstance(original_dict, OrderedDict): - calced_order = original_dict - elif _order_ is None: - calced_order = [name for (name, value) in enumsort(list(enum_members.items()))] - elif isinstance(_order_, basestring): - calced_order = _order_ = _order_.replace(',', ' ').split() - elif callable(_order_): - if init: - if not isinstance(init, basestring): - init = ' '.join(init) - member = NamedTuple('member', init and 'name ' + init or ['name', 'value']) - calced_order = [] - for name, value in enum_members.items(): - if init: - if not isinstance(value, tuple): - value = (value, ) - name_value = (name, ) + value - else: - name_value = tuple((name, value)) - if member._defined_len_ != len(name_value): - raise TypeError('%d values expected (%s), %d received (%s)' % ( - member._defined_len_, - ', '.join(member._fields_), - len(name_value), - ', '.join([repr(v) for v in name_value]), - )) - calced_order.append(member(*name_value)) - calced_order = [m.name for m in sorted(calced_order, key=_order_)] - else: - calced_order = _order_ - for name in ( - '_missing_', '_missing_value_', '_missing_name_', - '_ignore_', '_create_pseudo_member_', '_create_pseudo_member_values_', - '_generate_next_value_', '_order_', '__new__', - '_missing_', '_missing_value_', '_missing_name_', - '_boundary_', - '_iter_member_', '_iter_member_by_value_', '_iter_member_by_def_', - ): - attr = locals()[name] - if attr is not None: - clsdict[name] = attr - # now add members - for k in calced_order: - try: - clsdict[k] = original_dict[k] - except KeyError: - # this error will be handled when _order_ is checked - pass - for k, v in original_dict.items(): - if k not in calced_order: - clsdict[k] = v - del _order_, _ignore_, _create_pseudo_member_, _create_pseudo_member_values_, - del _generate_next_value_, _missing_, _missing_value_, _missing_name_ - # - # resume normal path - clsdict._locked = True - # - # check for illegal enum names (any others?) - member_names = clsdict._member_names - invalid_names = set(member_names) & set(['mro', '']) - if invalid_names: - raise ValueError('invalid enum member name(s): %s' % ( - ', '.join(invalid_names), )) - _order_ = clsdict.pop('_order_', None) - if isinstance(_order_, basestring): - _order_ = _order_.replace(',',' ').split() - init = clsdict._init - start = clsdict._start - settings = clsdict._settings - creating_init = [] - new_args = clsdict._new_args - auto_args = clsdict._auto_args - auto_init = False - if init is not None: - auto_init = True - creating_init = init[:] - if 'value' in creating_init and creating_init[0] != 'value': - raise TypeError("'value', if specified, must be the first item in 'init'") - magicvalue = MagicValue in settings - multivalue = MultiValue in settings - noalias = NoAlias in settings - unique = Unique in settings - # an Enum class cannot be mixed with other types (int, float, etc.) if - # it has an inherited __new__ unless a new __new__ is defined (or - # the resulting class will fail). - # an Enum class is final once enumeration items have been defined; - # - # remove any keys listed in _ignore_ - clsdict.setdefault('_ignore_', []).append('_ignore_') - ignore = clsdict['_ignore_'] - for key in ignore: - clsdict.pop(key, None) - # - boundary = boundary or clsdict.pop('_boundary_', None) - # convert to regular dict - clsdict = dict(clsdict.items()) - member_type, first_enum = metacls._get_mixins_(cls, bases) - # get the method to create enum members - __new__, save_new, new_uses_args = metacls._find_new_( - clsdict, - member_type, - first_enum, - ) - clsdict['_new_member_'] = staticmethod(__new__) - clsdict['_use_args_'] = new_uses_args - # - # convert future enum members into temporary _proto_members - # and record integer values in case this will be a Flag - flag_mask = 0 - for name in member_names: - value = test_value = clsdict[name] - if isinstance(value, auto) and value.value is not _auto_null: - test_value = value.value - if isinstance(test_value, baseinteger): - flag_mask |= test_value - if isinstance(test_value, tuple) and test_value and isinstance(test_value[0], baseinteger): - flag_mask |= test_value[0] - clsdict[name] = _proto_member(value) - # - # temp stuff - clsdict['_creating_init_'] = creating_init - clsdict['_multivalue_'] = multivalue - clsdict['_magicvalue_'] = magicvalue - clsdict['_noalias_'] = noalias - clsdict['_unique_'] = unique - # - # house-keeping structures - clsdict['_member_names_'] = [] - clsdict['_member_map_'] = OrderedDict() - clsdict['_member_type_'] = member_type - clsdict['_value2member_map_'] = {} - clsdict['_value2member_seq_'] = () - clsdict['_settings_'] = settings - clsdict['_start_'] = start - clsdict['_auto_init_'] = init - clsdict['_new_args_'] = new_args - clsdict['_auto_args_'] = auto_args - clsdict['_order_function_'] = None - # now set the __repr__ for the value - clsdict['_value_repr_'] = metacls._find_data_repr_(cls, bases) - # - # Flag structures (will be removed if final class is not a Flag - clsdict['_boundary_'] = ( - boundary - or getattr(first_enum, '_boundary_', None) - ) - clsdict['_flag_mask_'] = flag_mask - clsdict['_all_bits_'] = 2 ** ((flag_mask).bit_length()) - 1 - clsdict['_inverted_'] = None - # - # move skipped values out of the descriptor - for name, obj in clsdict.items(): - if isinstance(obj, nonmember): - clsdict[name] = obj.value - # - # If a custom type is mixed into the Enum, and it does not know how - # to pickle itself, pickle.dumps will succeed but pickle.loads will - # fail. Rather than have the error show up later and possibly far - # from the source, sabotage the pickle protocol for this class so - # that pickle.dumps also fails. - # - # However, if the new class implements its own __reduce_ex__, do not - # sabotage -- it's on them to make sure it works correctly. We use - # __reduce_ex__ instead of any of the others as it is preferred by - # pickle over __reduce__, and it handles all pickle protocols. - unpicklable = False - if '__reduce_ex__' not in clsdict: - if member_type is not object: - methods = ('__getnewargs_ex__', '__getnewargs__', - '__reduce_ex__', '__reduce__') - if not any(m in member_type.__dict__ for m in methods): - _make_class_unpicklable(clsdict) - unpicklable = True - # - # create a default docstring if one has not been provided - if '__doc__' not in clsdict: - clsdict['__doc__'] = 'An enumeration.' - # - # create our new Enum type - try: - exc = None - enum_class = type.__new__(metacls, cls, bases, clsdict) - except RuntimeError as e: - # any exceptions raised by _proto_member (aka member.__new__) will get converted to - # a RuntimeError, so get that original exception back and raise - # it instead - exc = e.__cause__ or e - if exc is not None: - raise exc - # - # if Python 3.5 or ealier, implement the __set_name__ and - # __init_subclass__ protocols - if pyver < PY3_6: - for name in member_names: - enum_class.__dict__[name].__set_name__(enum_class, name) - for name, obj in enum_class.__dict__.items(): - if name in member_names: - continue - if hasattr(obj, '__set_name__'): - obj.__set_name__(enum_class, name) - if Enum is not None: - super(enum_class, enum_class).__init_subclass__() - # - # double check that repr and friends are not the mixin's or various - # things break (such as pickle) - # - # Also, special handling for ReprEnum - if ReprEnum is not None and ReprEnum in bases: - if member_type is object: - raise TypeError( - 'ReprEnum subclasses must be mixed with a data type (i.e.' - ' int, str, float, etc.)' - ) - if '__format__' not in clsdict: - enum_class.__format__ = member_type.__format__ - clsdict['__format__'] = enum_class.__format__ - if '__str__' not in clsdict: - method = member_type.__str__ - if method is object.__str__: - # if member_type does not define __str__, object.__str__ will use - # its __repr__ instead, so we'll also use its __repr__ - method = member_type.__repr__ - enum_class.__str__ = method - clsdict['__str__'] = enum_class.__str__ - - for name in ('__repr__', '__str__', '__format__', '__reduce_ex__'): - if name in clsdict: - # class has defined/imported/copied the method - continue - class_method = getattr(enum_class, name) - obj_method = getattr(member_type, name, None) - enum_method = getattr(first_enum, name, None) - if obj_method is not None and obj_method == class_method: - if name == '__reduce_ex__' and unpicklable: - continue - setattr(enum_class, name, enum_method) - clsdict[name] = enum_method - # - # for Flag, add __or__, __and__, __xor__, and __invert__ - if Flag is not None and issubclass(enum_class, Flag): - for name in ( - '__or__', '__and__', '__xor__', - '__ror__', '__rand__', '__rxor__', - '__invert__' - ): - if name not in clsdict: - setattr(enum_class, name, getattr(Flag, name)) - clsdict[name] = enum_method - # - # method resolution and int's are not playing nice - # Python's less than 2.6 use __cmp__ - if pyver < PY2_6: - # - if issubclass(enum_class, int): - setattr(enum_class, '__cmp__', getattr(int, '__cmp__')) - # - elif PY2: - # - if issubclass(enum_class, int): - for method in ( - '__le__', - '__lt__', - '__gt__', - '__ge__', - '__eq__', - '__ne__', - '__hash__', - ): - setattr(enum_class, method, getattr(int, method)) - # - # replace any other __new__ with our own (as long as Enum is not None, - # anyway) -- again, this is to support pickle - if Enum is not None: - # if the user defined their own __new__, save it before it gets - # clobbered in case they subclass later - if save_new: - setattr(enum_class, '__new_member__', enum_class.__dict__['__new__']) - setattr(enum_class, '__new__', Enum.__dict__['__new__']) - # - # _order_ checking is spread out into three/four steps - # - ensure _order_ is a list, not a string nor a function - # - if enum_class is a Flag: - # - remove any non-single-bit flags from _order_ - # - remove any aliases from _order_ - # - check that _order_ and _member_names_ match - # - # _order_ step 1: ensure _order_ is a list - if _order_: - if isinstance(_order_, staticmethod): - _order_ = _order_.__func__ - if callable(_order_): - # save order for future subclasses - enum_class._order_function_ = staticmethod(_order_) - # create ordered list for comparison - _order_ = [m.name for m in sorted(enum_class, key=_order_)] - # - # remove Flag structures if final class is not a Flag - if ( - Flag is None and cls != 'Flag' - or Flag is not None and not issubclass(enum_class, Flag) - ): - delattr(enum_class, '_boundary_') - delattr(enum_class, '_flag_mask_') - delattr(enum_class, '_all_bits_') - delattr(enum_class, '_inverted_') - elif Flag is not None and issubclass(enum_class, Flag): - # ensure _all_bits_ is correct and there are no missing flags - single_bit_total = 0 - multi_bit_total = 0 - for flag in enum_class._member_map_.values(): - if _is_single_bit(flag._value_): - single_bit_total |= flag._value_ - else: - # multi-bit flags are considered aliases - multi_bit_total |= flag._value_ - if enum_class._boundary_ is not KEEP: - missed = list(_iter_bits_lsb(multi_bit_total & ~single_bit_total)) - if missed: - raise TypeError( - 'invalid Flag %r -- missing values: %s' - % (cls, ', '.join((str(i) for i in missed))) - ) - enum_class._flag_mask_ = single_bit_total - enum_class._all_bits_ = 2 ** ((single_bit_total).bit_length()) - 1 - # - # set correct __iter__ - if [m._value_ for m in enum_class] != sorted([m._value_ for m in enum_class]): - enum_class._iter_member_ = enum_class._iter_member_by_def_ - if _order_: - # _order_ step 2: remove any items from _order_ that are not single-bit - _order_ = [ - o - for o in _order_ - if o not in enum_class._member_map_ or _is_single_bit(enum_class[o]._value_) - ] - # - # check for constants with auto() values - for k, v in enum_class.__dict__.items(): - if isinstance(v, constant) and isinstance(v.value, auto): - v.value = enum_class(v.value.value) - # - if _order_: - # _order_ step 3: remove aliases from _order_ - _order_ = [ - o - for o in _order_ - if ( - o not in enum_class._member_map_ - or - (o in enum_class._member_map_ and o in enum_class._member_names_) - )] - # _order_ step 4: verify that _order_ and _member_names_ match - if _order_ != enum_class._member_names_: - raise TypeError( - 'member order does not match _order_:\n%r\n%r' - % (enum_class._member_names_, _order_) - ) - return enum_class - - def __bool__(cls): - """ - classes/types should always be True. - """ - return True - - def __call__(cls, value=no_arg, names=None, module=None, qualname=None, type=None, start=1, boundary=None): - """Either returns an existing member, or creates a new enum class. - - This method is used both when an enum class is given a value to match - to an enumeration member (i.e. Color(3)) and for the functional API - (i.e. Color = Enum('Color', names='red green blue')). - - When used for the functional API: `module`, if set, will be stored in - the new class' __module__ attribute; `type`, if set, will be mixed in - as the first base class. - - Note: if `module` is not set this routine will attempt to discover the - calling module by walking the frame stack; if this is unsuccessful - the resulting class will not be pickleable. - """ - if names is None: # simple value lookup - return cls.__new__(cls, value) - # otherwise, functional API: we're creating a new Enum type - return cls._create_(value, names, module=module, qualname=qualname, type=type, start=start, boundary=boundary) - - def __contains__(cls, member): - if not isinstance(member, Enum): - raise TypeError("%r (%r) is not an " % (member, type(member))) - if not isinstance(member, cls): - return False - return True - - def __delattr__(cls, attr): - # nicer error message when someone tries to delete an attribute - # (see issue19025). - if attr in cls._member_map_: - raise AttributeError( - "%s: cannot delete Enum member %r." % (cls.__name__, attr), - ) - found_attr = _get_attr_from_chain(cls, attr) - if isinstance(found_attr, constant): - raise AttributeError( - "%s: cannot delete constant %r" % (cls.__name__, attr), - ) - elif isinstance(found_attr, property): - raise AttributeError( - "%s: cannot delete property %r" % (cls.__name__, attr), - ) - super(EnumType, cls).__delattr__(attr) - - def __dir__(cls): - interesting = set(cls._member_names_ + [ - '__class__', '__contains__', '__doc__', '__getitem__', - '__iter__', '__len__', '__members__', '__module__', - '__name__', - ]) - if cls._new_member_ is not object.__new__: - interesting.add('__new__') - if cls.__init_subclass__ is not Enum.__init_subclass__: - interesting.add('__init_subclass__') - if hasattr(object, '__qualname__'): - interesting.add('__qualname__') - for method in ('__init__', '__format__', '__repr__', '__str__'): - if getattr(cls, method) not in (getattr(Enum, method), getattr(Flag, method)): - interesting.add(method) - if cls._member_type_ is object: - return sorted(interesting) - else: - # return whatever mixed-in data type has - return sorted(set(dir(cls._member_type_)) | interesting) - - @_bltin_property - def __members__(cls): - """Returns a mapping of member name->value. - - This mapping lists all enum members, including aliases. Note that this - is a copy of the internal mapping. - """ - return cls._member_map_.copy() - - def __getitem__(cls, name): - try: - return cls._member_map_[name] - except KeyError: - exc = _sys.exc_info()[1] - if Flag is not None and issubclass(cls, Flag) and '|' in name: - try: - # may be an __or__ed name - result = cls(0) - for n in name.split('|'): - result |= cls[n] - return result - except KeyError: - raise exc - result = cls._missing_name_(name) - if isinstance(result, cls): - return result - else: - raise exc - - def __iter__(cls): - return (cls._member_map_[name] for name in cls._member_names_) - - def __reversed__(cls): - return (cls._member_map_[name] for name in reversed(cls._member_names_)) - - def __len__(cls): - return len(cls._member_names_) - - __nonzero__ = __bool__ - - def __repr__(cls): - return "" % (cls.__name__, ) - - def __setattr__(cls, name, value): - """Block attempts to reassign Enum members/constants. - - A simple assignment to the class namespace only changes one of the - several possible ways to get an Enum member from the Enum class, - resulting in an inconsistent Enumeration. - """ - member_map = cls.__dict__.get('_member_map_', {}) - if name in member_map: - raise AttributeError( - '%s: cannot rebind member %r.' % (cls.__name__, name), - ) - found_attr = _get_attr_from_chain(cls, name) - if isinstance(found_attr, constant): - raise AttributeError( - "%s: cannot rebind constant %r" % (cls.__name__, name), - ) - elif isinstance(found_attr, property): - raise AttributeError( - "%s: cannot rebind property %r" % (cls.__name__, name), - ) - super(EnumType, cls).__setattr__(name, value) - - def _convert(cls, *args, **kwds): - import warnings - warnings.warn("_convert is deprecated and will be removed, use" - " _convert_ instead.", DeprecationWarning, stacklevel=2) - return cls._convert_(*args, **kwds) - - def _convert_(cls, name, module, filter, source=None, boundary=None, as_global=False): - """ - Create a new Enum subclass that replaces a collection of global constants - """ - # convert all constants from source (or module) that pass filter() to - # a new Enum called name, and export the enum and its members back to - # module; - # also, replace the __reduce_ex__ method so unpickling works in - # previous Python versions - module_globals = vars(_sys.modules[module]) - if source: - source = vars(source) - else: - source = module_globals - members = [(key, source[key]) for key in source.keys() if filter(key)] - try: - # sort by value, name - members.sort(key=lambda t: (t[1], t[0])) - except TypeError: - # unless some values aren't comparable, in which case sort by just name - members.sort(key=lambda t: t[0]) - cls = cls(name, members, module=module, boundary=boundary or KEEP) - cls.__reduce_ex__ = _reduce_ex_by_name - if as_global: - global_enum(cls) - else: - module_globals.update(cls.__members__) - module_globals[name] = cls - return cls - - def _create_(cls, class_name, names, module=None, qualname=None, type=None, start=1, boundary=None): - """Convenience method to create a new Enum class. - - `names` can be: - - * A string containing member names, separated either with spaces or - commas. Values are auto-numbered from 1. - * An iterable of member names. Values are auto-numbered from 1. - * An iterable of (member name, value) pairs. - * A mapping of member name -> value. - """ - if PY2: - # if class_name is unicode, attempt a conversion to ASCII - if isinstance(class_name, unicode): - try: - class_name = class_name.encode('ascii') - except UnicodeEncodeError: - raise TypeError('%r is not representable in ASCII' % (class_name, )) - metacls = cls.__class__ - if type is None: - bases = (cls, ) - else: - bases = (type, cls) - _, first_enum = cls._get_mixins_(class_name, bases) - generate = getattr(first_enum, '_generate_next_value_', None) - generate = getattr(generate, 'im_func', generate) - # special processing needed for names? - if isinstance(names, basestring): - names = names.replace(',', ' ').split() - if isinstance(names, (tuple, list)) and names and isinstance(names[0], basestring): - original_names, names = names, [] - last_values = [] - for count, name in enumerate(original_names): - value = generate(name, start, count, last_values[:]) - last_values.append(value) - names.append((name, value)) - # Here, names is either an iterable of (name, value) or a mapping. - item = None # in case names is empty - clsdict = None - for item in names: - if clsdict is None: - # first time initialization - if isinstance(item, basestring): - clsdict = {} - else: - # remember the order - clsdict = metacls.__prepare__(class_name, bases) - if isinstance(item, basestring): - member_name, member_value = item, names[item] - else: - member_name, member_value = item - clsdict[member_name] = member_value - if clsdict is None: - # in case names was empty - clsdict = metacls.__prepare__(class_name, bases) - enum_class = metacls.__new__(metacls, class_name, bases, clsdict, boundary=boundary) - # TODO: replace the frame hack if a blessed way to know the calling - # module is ever developed - if module is None: - try: - module = _sys._getframe(2).f_globals['__name__'] - except (AttributeError, KeyError): - pass - if module is None: - _make_class_unpicklable(enum_class) - else: - enum_class.__module__ = module - if qualname is not None: - enum_class.__qualname__ = qualname - return enum_class - - @classmethod - def _check_for_existing_members_(mcls, class_name, bases): - if Enum is None: - return - for chain in bases: - for base in chain.__mro__: - if issubclass(base, Enum) and base._member_names_: - raise TypeError( - " cannot extend %r" - % (class_name, base) - ) - @classmethod - def _get_mixins_(mcls, class_name, bases): - """Returns the type for creating enum members, and the first inherited - enum class. - - bases: the tuple of bases that was given to __new__ - """ - if not bases or Enum is None: - return object, Enum - - mcls._check_for_existing_members_(class_name, bases) - - # ensure final parent class is an Enum derivative, find any concrete - # data type, and check that Enum has no members - first_enum = bases[-1] - if not issubclass(first_enum, Enum): - raise TypeError("new enumerations should be created as " - "`EnumName([mixin_type, ...] [data_type,] enum_type)`") - member_type = mcls._find_data_type_(class_name, bases) or object - if first_enum._member_names_: - raise TypeError("cannot extend enumerations via subclassing") - # - return member_type, first_enum - - @classmethod - def _find_data_repr_(mcls, class_name, bases): - for chain in bases: - for base in chain.__mro__: - if base is object: - continue - elif issubclass(base, Enum): - # if we hit an Enum, use it's _value_repr_ - return base._value_repr_ - elif '__repr__' in base.__dict__: - # this is our data repr - return base.__dict__['__repr__'] - return None - - @classmethod - def _find_data_type_(mcls, class_name, bases): - data_types = set() - for chain in bases: - candidate = None - for base in chain.__mro__: - if base is object or base is StdlibEnum or base is StdlibFlag: - continue - elif issubclass(base, Enum): - if base._member_type_ is not object: - data_types.add(base._member_type_) - elif '__new__' in base.__dict__: - if issubclass(base, Enum): - continue - elif StdlibFlag is not None and issubclass(base, StdlibFlag): - continue - data_types.add(candidate or base) - break - else: - candidate = candidate or base - if len(data_types) > 1: - raise TypeError('%r: too many data types: %r' % (class_name, data_types)) - elif data_types: - return data_types.pop() - else: - return None - - @staticmethod - def _get_settings_(bases): - """Returns the combined _settings_ of all Enum base classes - - bases: the tuple of bases given to __new__ - """ - settings = set() - for chain in bases: - for base in chain.__mro__: - if issubclass(base, Enum): - for s in base._settings_: - settings.add(s) - return settings - - @classmethod - def _find_new_(mcls, clsdict, member_type, first_enum): - """Returns the __new__ to be used for creating the enum members. - - clsdict: the class dictionary given to __new__ - member_type: the data type whose __new__ will be used by default - first_enum: enumeration to check for an overriding __new__ - """ - # now find the correct __new__, checking to see of one was defined - # by the user; also check earlier enum classes in case a __new__ was - # saved as __new_member__ - __new__ = clsdict.get('__new__', None) - # - # should __new__ be saved as __new_member__ later? - save_new = first_enum is not None and __new__ is not None - # - if __new__ is None: - # check all possibles for __new_member__ before falling back to - # __new__ - for method in ('__new_member__', '__new__'): - for possible in (member_type, first_enum): - target = getattr(possible, method, None) - if target not in ( - None, - None.__new__, - object.__new__, - Enum.__new__, - StdlibEnum.__new__, - ): - __new__ = target - break - if __new__ is not None: - break - else: - __new__ = object.__new__ - # if a non-object.__new__ is used then whatever value/tuple was - # assigned to the enum member name will be passed to __new__ and to the - # new enum member's __init__ - if __new__ is object.__new__: - new_uses_args = False - else: - new_uses_args = True - # - return __new__, save_new, new_uses_args - - - # In order to support Python 2 and 3 with a single - # codebase we have to create the Enum methods separately - # and then use the `type(name, bases, dict)` method to - # create the class. - -EnumMeta = EnumType - -enum_dict = _Addendum( - dict=EnumType.__prepare__('Enum', (object, )), - doc="Generic enumeration.\n\n Derive from this class to define new enumerations.\n\n", - ns=globals(), - ) - -@enum_dict -def __init__(self, *args, **kwds): - # auto-init method - _auto_init_ = self._auto_init_ - if _auto_init_ is None: - return - if 'value' in _auto_init_: - # remove 'value' from _auto_init_ as it has already been handled - _auto_init_ = _auto_init_[1:] - if _auto_init_: - if len(_auto_init_) < len(args): - raise TypeError('%d arguments expected (%s), %d received (%s)' - % (len(_auto_init_), _auto_init_, len(args), args)) - for name, arg in zip(_auto_init_, args): - setattr(self, name, arg) - if len(args) < len(_auto_init_): - remaining_args = _auto_init_[len(args):] - for name in remaining_args: - value = kwds.pop(name, undefined) - if value is undefined: - raise TypeError('missing value for: %r' % (name, )) - setattr(self, name, value) - if kwds: - # too many keyword arguments - raise TypeError('invalid keyword(s): %s' % ', '.join(kwds.keys())) - -@enum_dict -def __new__(cls, value): - # all enum instances are actually created during class construction - # without calling this method; this method is called by the metaclass' - # __call__ (i.e. Color(3) ), and by pickle - if NoAlias in cls._settings_: - raise TypeError('NoAlias enumerations cannot be looked up by value') - if type(value) is cls: - # For lookups like Color(Color.red) - # value = value.value - return value - # by-value search for a matching enum member - # see if it's in the reverse mapping (for hashable values) - try: - if value in cls._value2member_map_: - return cls._value2member_map_[value] - except TypeError: - # not there, now do long search -- O(n) behavior - for name, member in cls._value2member_seq_: - if name == value: - return member - # still not found -- try _missing_ hook - try: - exc = None - result = cls._missing_value_(value) - except Exception as e: - exc = e - result = None - if isinstance(result, cls) or getattr(cls, '_boundary_', None) is EJECT: - return result - else: - if value is no_arg: - ve_exc = ValueError('%s() should be called with a value' % (cls.__name__, )) - else: - ve_exc = ValueError("%r is not a valid %s" % (value, cls.__name__)) - if result is None and exc is None: - raise ve_exc - elif exc is None: - exc = TypeError( - 'error in %s._missing_: returned %r instead of None or a valid member' - % (cls.__name__, result) - ) - if not isinstance(exc, ValueError): - exc.__cause__ = ve_exc - raise exc - -@enum_dict -@classmethod -def __init_subclass__(cls, **kwds): - if pyver < PY3_6: - # end of the line - if kwds: - raise TypeError('unconsumed keyword arguments: %r' % (kwds, )) - else: - super(Enum, cls).__init_subclass__(**kwds) - -@enum_dict -@staticmethod -def _generate_next_value_(name, start, count, last_values, *args, **kwds): - for last_value in reversed(last_values): - try: - new_value = last_value + 1 - break - except TypeError: - pass - else: - new_value = start - if args: - return (new_value, ) + args - else: - return new_value - -@enum_dict -@classmethod -def _missing_(cls, value): - "deprecated, use _missing_value_ instead" - return None - -@enum_dict -@classmethod -def _missing_value_(cls, value): - "used for failed value access" - return cls._missing_(value) - -@enum_dict -@classmethod -def _missing_name_(cls, name): - "used for failed item access" - return None - -@enum_dict -def __repr__(self): - v_repr = self.__class__._value_repr_ or self._value_.__class__.__repr__ - return "<%s.%s: %s>" % (self.__class__.__name__, self._name_, v_repr(self._value_)) - -@enum_dict -def __str__(self): - return "%s.%s" % (self.__class__.__name__, self._name_) - -if PY3: - @enum_dict - def __dir__(self): - """ - Returns all members and all public methods - """ - if self.__class__._member_type_ is object: - interesting = set(['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'value']) - else: - interesting = set(object.__dir__(self)) - for name in getattr(self, '__dict__', []): - if name[0] != '_': - interesting.add(name) - for cls in self.__class__.mro(): - for name, obj in cls.__dict__.items(): - if name[0] == '_': - continue - if isinstance(obj, property): - # that's an enum.property - if obj.fget is not None or name not in self._member_map_: - interesting.add(name) - else: - # in case it was added by `dir(self)` - interesting.discard(name) - else: - interesting.add(name) - return sorted(interesting) - -@enum_dict -def __format__(self, format_spec): - # mixed-in Enums should use the mixed-in type's __format__, otherwise - # we can get strange results with the Enum name showing up instead of - # the value - - # pure Enum branch / overridden __str__ branch - overridden_str = self.__class__.__str__ != Enum.__str__ - if self._member_type_ is object or overridden_str: - cls = str - val = str(self) - # mix-in branch - else: - cls = self._member_type_ - val = self.value - return cls.__format__(val, format_spec) - -@enum_dict -def __hash__(self): - return hash(self._name_) - -@enum_dict -def __reduce_ex__(self, proto): - return self.__class__, (self._value_, ) - - -#################################### -# Python's less than 2.6 use __cmp__ - -if pyver < PY2_6: - - @enum_dict - def __cmp__(self, other): - if type(other) is self.__class__: - if self is other: - return 0 - return -1 - return NotImplemented - raise TypeError("unorderable types: %s() and %s()" % (self.__class__.__name__, other.__class__.__name__)) - -else: - - @enum_dict - def __le__(self, other): - raise TypeError("unorderable types: %s() <= %s()" % (self.__class__.__name__, other.__class__.__name__)) - - @enum_dict - def __lt__(self, other): - raise TypeError("unorderable types: %s() < %s()" % (self.__class__.__name__, other.__class__.__name__)) - - @enum_dict - def __ge__(self, other): - raise TypeError("unorderable types: %s() >= %s()" % (self.__class__.__name__, other.__class__.__name__)) - - @enum_dict - def __gt__(self, other): - raise TypeError("unorderable types: %s() > %s()" % (self.__class__.__name__, other.__class__.__name__)) - - -@enum_dict -def __eq__(self, other): - if type(other) is self.__class__: - return self is other - return NotImplemented - -@enum_dict -def __ne__(self, other): - if type(other) is self.__class__: - return self is not other - return NotImplemented - -@enum_dict -def __hash__(self): - return hash(self._name_) - -@enum_dict -def __reduce_ex__(self, proto): - return self.__class__, (self._value_, ) - - -# enum.property is used to provide access to the `name`, `value', etc., -# properties of enum members while keeping some measure of protection -# from modification, while still allowing for an enumeration to have -# members named `name`, `value`, etc.. This works because enumeration -# members are not set directly on the enum class -- enum.property will -# look them up in _member_map_. - -@enum_dict -@property -def name(self): - return self._name_ - -@enum_dict -@property -def value(self): - return self._value_ - -@enum_dict -@property -def values(self): - return self._values_ - -def _reduce_ex_by_name(self, proto): - return self.name - -Enum = EnumType('Enum', (object, ), enum_dict.resolve()) -del enum_dict - - # Enum has now been created - -class ReprEnum(Enum): - """ - Only changes the repr(), leaving str() and format() to the mixed-in type. - """ - - -class IntEnum(int, ReprEnum): - """ - Enum where members are also (and must be) ints - """ - - -class StrEnum(str, ReprEnum): - """ - Enum where members are also (and must already be) strings - - default value is member name, lower-cased - """ - - def __new__(cls, *values, **kwds): - if kwds: - raise TypeError('%r: keyword arguments not supported' % (cls.__name__)) - if values: - if not isinstance(values[0], str): - raise TypeError('%s: values must be str [%r is a %r]' % (cls.__name__, values[0], type(values[0]))) - value = str(*values) - member = str.__new__(cls, value) - member._value_ = value - return member - - __str__ = str.__str__ - - def _generate_next_value_(name, start, count, last_values): - """ - Return the lower-cased version of the member name. - """ - return name.lower() - - -class LowerStrEnum(StrEnum): - """ - Enum where members are also (and must already be) lower-case strings - - default value is member name, lower-cased - """ - - def __new__(cls, value, *args, **kwds): - obj = StrEnum.__new_member__(cls, value, *args, **kwds) - if value != value.lower(): - raise ValueError('%r is not lower-case' % value) - return obj - - -class UpperStrEnum(StrEnum): - """ - Enum where members are also (and must already be) upper-case strings - - default value is member name, upper-cased - """ - - def __new__(cls, value, *args, **kwds): - obj = StrEnum.__new_member__(cls, value, *args, **kwds) - if value != value.upper(): - raise ValueError('%r is not upper-case' % value) - return obj - - def _generate_next_value_(name, start, count, last_values, *args, **kwds): - return name.upper() - - -if PY3: - class AutoEnum(Enum): - """ - automatically use _generate_next_value_ when values are missing (Python 3 only) - """ - _settings_ = MagicValue - - -class AutoNumberEnum(Enum): - """ - Automatically assign increasing values to members. - - Py3: numbers match creation order - Py2: numbers are assigned alphabetically by member name - (unless `_order_` is specified) - """ - - def __new__(cls, *args, **kwds): - value = len(cls.__members__) + 1 - if cls._member_type_ is int: - obj = int.__new__(cls, value) - elif cls._member_type_ is long: - obj = long.__new__(cls, value) - else: - obj = object.__new__(cls) - obj._value_ = value - return obj - - -class AddValueEnum(Enum): - _settings_ = AddValue - - -class MultiValueEnum(Enum): - """ - Multiple values can map to each member. - """ - _settings_ = MultiValue - - -class NoAliasEnum(Enum): - """ - Duplicate value members are distinct, but cannot be looked up by value. - """ - _settings_ = NoAlias - - -class OrderedEnum(Enum): - """ - Add ordering based on values of Enum members. - """ - - def __ge__(self, other): - if self.__class__ is other.__class__: - return self._value_ >= other._value_ - return NotImplemented - - def __gt__(self, other): - if self.__class__ is other.__class__: - return self._value_ > other._value_ - return NotImplemented - - def __le__(self, other): - if self.__class__ is other.__class__: - return self._value_ <= other._value_ - return NotImplemented - - def __lt__(self, other): - if self.__class__ is other.__class__: - return self._value_ < other._value_ - return NotImplemented - - -if sqlite3: - class SqliteEnum(Enum): - def __conform__(self, protocol): - if protocol is sqlite3.PrepareProtocol: - return self.name - - -class UniqueEnum(Enum): - """ - Ensure no duplicate values exist. - """ - _settings_ = Unique - - -def convert(enum, name, module, filter, source=None): - """ - Create a new Enum subclass that replaces a collection of global constants - - enum: Enum, IntEnum, ... - name: name of new Enum - module: name of module (__name__ in global context) - filter: function that returns True if name should be converted to Enum member - source: namespace to check (defaults to 'module') - """ - # convert all constants from source (or module) that pass filter() to - # a new Enum called name, and export the enum and its members back to - # module; - # also, replace the __reduce_ex__ method so unpickling works in - # previous Python versions - module_globals = vars(_sys.modules[module]) - if source: - source = vars(source) - else: - source = module_globals - members = dict((name, value) for name, value in source.items() if filter(name)) - enum = enum(name, members, module=module) - enum.__reduce_ex__ = _reduce_ex_by_name - module_globals.update(enum.__members__) - module_globals[name] = enum - -def extend_enum(enumeration, name, *args, **kwds): - """ - Add a new member to an existing Enum. - """ - # there are four possibilities: - # - extending an aenum Enum or 3.11+ enum Enum - # - extending an aenum Flag or 3.11+ enum Flag - # - extending a pre-3.11 stdlib Enum Flag - # - extending a 3.11+ stdlib Flag - # - # fail early if name is already in the enumeration - if ( - name in enumeration.__dict__ - or name in enumeration._member_map_ - or name in [t[1] for t in getattr(enumeration, '_value2member_seq_', ())] - ): - raise TypeError('%r already in use as %r' % (name, enumeration.__dict__.get(name, enumeration[name]))) - # and check for other instances in parent classes - descriptor = None - for base in enumeration.__mro__[1:]: - descriptor = base.__dict__.get(name) - if descriptor is not None: - if isinstance(descriptor, (property, DynamicClassAttribute)): - break - else: - raise TypeError('%r already in use in superclass %r' % (name, base.__name__)) - try: - _member_map_ = enumeration._member_map_ - _member_names_ = enumeration._member_names_ - _member_type_ = enumeration._member_type_ - _value2member_map_ = enumeration._value2member_map_ - base_attributes = set([a for b in enumeration.mro() for a in b.__dict__]) - except AttributeError: - raise TypeError('%r is not a supported Enum' % (enumeration, )) - try: - _value2member_seq_ = enumeration._value2member_seq_ - _multi_value_ = MultiValue in enumeration._settings_ - _no_alias_ = NoAlias in enumeration._settings_ - _unique_ = Unique in enumeration._settings_ - _auto_init_ = enumeration._auto_init_ or [] - except AttributeError: - # standard Enum - _value2member_seq_ = [] - _multi_value_ = False - _no_alias_ = False - _unique_ = False - _auto_init_ = [] - if _multi_value_ and not args: - # must specify values for multivalue enums - raise ValueError('no values specified for MultiValue enum %r' % enumeration.__name__) - mt_new = _member_type_.__new__ - _new = getattr(enumeration, '__new_member__', mt_new) - if not args: - last_values = [m.value for m in enumeration] - count = len(enumeration) - start = getattr(enumeration, '_start_', None) - if start is None: - start = last_values and (last_values[-1] + 1) or 1 - _gnv = getattr(enumeration, '_generate_next_value_', None) - if _gnv is not None: - args = ( _gnv(name, start, count, last_values), ) - else: - # must be a 3.4 or 3.5 Enum - args = (start, ) - if _new is object.__new__: - new_uses_args = False - else: - new_uses_args = True - if len(args) == 1: - [value] = args - else: - value = args - more_values = () - kwds = {} - if isinstance(value, enum): - args = value.args - kwds = value.kwds - if not isinstance(value, tuple): - args = (value, ) - else: - args = value - # tease value out of auto-init if specified - if 'value' in _auto_init_: - if 'value' in kwds: - value = kwds.pop('value') - else: - value, args = args[0], args[1:] - elif _multi_value_: - value, more_values, args = args[0], args[1:], () - if new_uses_args: - args = (value, ) - if _member_type_ is tuple: - args = (args, ) - if not new_uses_args: - new_member = _new(enumeration) - if not hasattr(new_member, '_value_'): - new_member._value_ = value - else: - new_member = _new(enumeration, *args, **kwds) - if not hasattr(new_member, '_value_'): - new_member._value_ = _member_type_(*args) - value = new_member._value_ - if _multi_value_: - if 'value' in _auto_init_: - args = more_values - else: - # put all the values back into args for the init call - args = (value, ) + more_values - new_member._name_ = name - new_member.__objclass__ = enumeration.__class__ - new_member.__init__(*args) - new_member._values_ = (value, ) + more_values - # do final checks before modifying enum structures: - # - is new member a flag? - # - does the new member fit in the enum's declared _boundary_? - # - is new member an alias? - # - _all_bits_ = _flag_mask_ = None - if hasattr(enumeration, '_all_bits_'): - _all_bits_ = enumeration._all_bits_ | value - _flag_mask_ = enumeration._flag_mask_ | value - if enumeration._boundary_ != 'keep': - missed = list(_iter_bits_lsb(_flag_mask_ & ~_all_bits_)) - if missed: - raise TypeError( - 'invalid Flag %r -- missing values: %s' - % (cls, ', '.join((str(i) for i in missed))) - ) - # If another member with the same value was already defined, the - # new member becomes an alias to the existing one. - if _no_alias_: - # unless NoAlias was specified - return _finalize_extend_enum(enumeration, new_member, bits=_all_bits_, mask=_flag_mask_) - else: - # handle "normal" aliases - new_values = new_member._values_ - for canonical_member in _member_map_.values(): - canonical_values_ = getattr(canonical_member, '_values_', [canonical_member._value_]) - for canonical_value in canonical_values_: - for new_value in new_values: - if canonical_value == new_value: - # name is an alias - if _unique_ or _multi_value_: - # aliases not allowed in Unique and MultiValue enums - raise ValueError('%r is a duplicate of %r' % (new_member, canonical_member)) - else: - # aliased name can be added, remaining checks irrelevant - # aliases don't appear in member names (only in __members__ and _member_map_). - return _finalize_extend_enum(enumeration, canonical_member, name=name, bits=_all_bits_, mask=_flag_mask_, is_alias=True) - # not a standard alias, but maybe a flag alias - if pyver < PY3_6: - flag_bases = Flag, - else: - flag_bases = Flag, StdlibFlag - if issubclass(enumeration, flag_bases) and hasattr(enumeration, '_all_bits_'): - # handle the new flag type - if _is_single_bit(value): - # a new member! (an aliase would have been discovered in the previous loop) - return _finalize_extend_enum(enumeration, new_member, bits=_all_bits_, mask=_flag_mask_) - else: - # might be an 3.11 Flag alias - if value & enumeration._flag_mask_ == value and _value2member_map_.get(value) is not None: - # yup, it's an alias to existing members... and its an alias of an alias - canonical = _value2member_map_.get(value) - return _finalize_extend_enum(enumeration, canonical, name=name, bits=_all_bits_, mask=_flag_mask_, is_alias=True) - else: - return _finalize_extend_enum(enumeration, new_member, bits=_all_bits_, mask=_flag_mask_, is_alias=True) - else: - # if we get here, we have a brand new member - return _finalize_extend_enum(enumeration, new_member) - -def _finalize_extend_enum(enumeration, new_member, name=None, bits=None, mask=None, is_alias=False): - name = name or new_member.name - descriptor = None - for base in enumeration.__mro__[1:]: - descriptor = base.__dict__.get(name) - if descriptor is not None: - if isinstance(descriptor, (property, DynamicClassAttribute)): - break - else: - raise TypeError('%r already in use in superclass %r' % (name, base.__name__)) - if not descriptor: - # get redirect in place before adding to _member_map_ - redirect = property() - redirect.__set_name__(enumeration, name) - setattr(enumeration, name, redirect) - if not is_alias: - enumeration._member_names_.append(name) - enumeration._member_map_[name] = new_member - for v in getattr(new_member, '_values_', [new_member._value_]): - try: - enumeration._value2member_map_[v] = new_member - except TypeError: - enumeration._value2member_seq_ += ((v, new_member), ) - if bits: - enumeration._all_bits_ = bits - enumeration._flag_mask_ = mask - return new_member - -def unique(enumeration): - """ - Class decorator that ensures only unique members exist in an enumeration. - """ - duplicates = [] - for name, member in enumeration.__members__.items(): - if name != member.name: - duplicates.append((name, member.name)) - if duplicates: - duplicate_names = ', '.join( - ["%s -> %s" % (alias, name) for (alias, name) in duplicates] - ) - raise ValueError('duplicate names found in %r: %s' % - (enumeration, duplicate_names) - ) - return enumeration - -# Flag - -@export(globals()) -class FlagBoundary(StrEnum): - """ - control how out of range values are handled - "strict" -> error is raised [default] - "conform" -> extra bits are discarded - "eject" -> lose flag status (becomes a normal integer) - """ - STRICT = auto() - CONFORM = auto() - EJECT = auto() - KEEP = auto() -assert FlagBoundary.STRICT == 'strict', (FlagBoundary.STRICT, FlagBoundary.CONFORM) - -class Flag(Enum): - """ - Generic flag enumeration. - - Derive from this class to define new flag enumerations. - """ - - _boundary_ = STRICT - _numeric_repr_ = repr - - - def _generate_next_value_(name, start, count, last_values, *args, **kwds): - """ - Generate the next value when not given. - - name: the name of the member - start: the initital start value or None - count: the number of existing members - last_value: the last value assigned or None - """ - if not count: - if args: - return ((1, start)[start is not None], ) + args - else: - return (1, start)[start is not None] - else: - last_value = max(last_values) - try: - high_bit = _high_bit(last_value) - result = 2 ** (high_bit+1) - if args: - return (result,) + args - else: - return result - except Exception: - pass - raise TypeError('invalid Flag value: %r' % last_value) - - @classmethod - def _iter_member_by_value_(cls, value): - """ - Extract all members from the value in definition (i.e. increasing value) order. - """ - for val in _iter_bits_lsb(value & cls._flag_mask_): - yield cls._value2member_map_.get(val) - - _iter_member_ = _iter_member_by_value_ - - @classmethod - def _iter_member_by_def_(cls, value): - """ - Extract all members from the value in definition order. - """ - members = list(cls._iter_member_by_value_(value)) - members.sort(key=lambda m: m._sort_order_) - for member in members: - yield member - - @classmethod - def _missing_(cls, value): - """ - return a member matching the given value, or None - """ - return cls._create_pseudo_member_(value) - - @classmethod - def _create_pseudo_member_(cls, *values): - """ - Create a composite member. - """ - value = values[0] - if not isinstance(value, baseinteger): - raise ValueError( - "%r is not a valid %s" % (value, getattr(cls, '__qualname__', cls.__name__)) - ) - # check boundaries - # - value must be in range (e.g. -16 <-> +15, i.e. ~15 <-> 15) - # - value must not include any skipped flags (e.g. if bit 2 is not - # defined, then 0d10 is invalid) - neg_value = None - if ( - not ~cls._all_bits_ <= value <= cls._all_bits_ - or value & (cls._all_bits_ ^ cls._flag_mask_) - ): - if cls._boundary_ is STRICT: - max_bits = max(value.bit_length(), cls._flag_mask_.bit_length()) - raise ValueError( - "%s: invalid value: %r\n given %s\n allowed %s" - % (cls.__name__, value, bin(value, max_bits), bin(cls._flag_mask_, max_bits)) - ) - elif cls._boundary_ is CONFORM: - value = value & cls._flag_mask_ - elif cls._boundary_ is EJECT: - return value - elif cls._boundary_ is KEEP: - if value < 0: - value = ( - max(cls._all_bits_+1, 2**(value.bit_length())) - + value - ) - else: - raise ValueError( - 'unknown flag boundary: %r' % (cls._boundary_, ) - ) - if value < 0: - neg_value = value - value = cls._all_bits_ + 1 + value - # get members and unknown - unknown = value & ~cls._flag_mask_ - members = list(cls._iter_member_(value)) - if unknown and cls._boundary_ is not KEEP: - raise ValueError( - '%s(%r) --> unknown values %r [%s]' - % (cls.__name__, value, unknown, bin(unknown)) - ) - # let class adjust values - values = cls._create_pseudo_member_values_(members, *values) - __new__ = getattr(cls, '__new_member__', None) - if cls._member_type_ is object and not __new__: - # construct a singleton enum pseudo-member - pseudo_member = object.__new__(cls) - else: - pseudo_member = (__new__ or cls._member_type_.__new__)(cls, *values) - if not hasattr(pseudo_member, 'value'): - pseudo_member._value_ = value - if members: - pseudo_member._name_ = '|'.join([m._name_ for m in members]) - if unknown: - pseudo_member._name_ += '|%s' % cls._numeric_repr_(unknown) - else: - pseudo_member._name_ = None - # use setdefault in case another thread already created a composite - # with this value, but only if all members are known - # note: zero is a special case -- add it - if not unknown: - pseudo_member = cls._value2member_map_.setdefault(value, pseudo_member) - if neg_value is not None: - cls._value2member_map_[neg_value] = pseudo_member - return pseudo_member - - - @classmethod - def _create_pseudo_member_values_(cls, members, *values): - """ - Return values to be fed to __new__ to create new member. - """ - if cls._member_type_ in (baseinteger + (object, )): - return values - elif len(values) < 2: - return values + (cls._member_type_(), ) - else: - return values - - def __contains__(self, other): - """ - Returns True if self has at least the same flags set as other. - """ - if not isinstance(other, self.__class__): - raise TypeError( - "unsupported operand type(s) for 'in': '%s' and '%s'" % ( - type(other).__name__, self.__class__.__name__)) - if other._value_ == 0 or self._value_ == 0: - return False - return other._value_ & self._value_ == other._value_ - - def __iter__(self): - """ - Returns flags in definition order. - """ - for member in self._iter_member_(self._value_): - yield member - - def __len__(self): - return _bit_count(self._value_) - - def __repr__(self): - cls = self.__class__ - if self._name_ is None: - # only zero is unnamed by default - return '<%s: %r>' % (cls.__name__, self._value_) - else: - return '<%s.%s: %r>' % (cls.__name__, self._name_, self._value_) - - def __str__(self): - cls = self.__class__ - if self._name_ is None: - return '%s(%s)' % (cls.__name__, self._value_) - else: - return '%s.%s' % (cls.__name__, self._name_) - - if PY2: - def __nonzero__(self): - return bool(self._value_) - else: - def __bool__(self): - return bool(self._value_) - - def __or__(self, other): - if isinstance(other, self.__class__): - other_value = other._value_ - elif self._member_type_ is not object and isinstance(other, self._member_type_): - other_value = other - else: - return NotImplemented - return self.__class__(self._value_ | other_value) - - def __and__(self, other): - if isinstance(other, self.__class__): - other_value = other._value_ - elif self._member_type_ is not object and isinstance(other, self._member_type_): - other_value = other - else: - return NotImplemented - return self.__class__(self._value_ & other_value) - - def __xor__(self, other): - if isinstance(other, self.__class__): - other_value = other._value_ - elif self._member_type_ is not object and isinstance(other, self._member_type_): - other_value = other - else: - return NotImplemented - return self.__class__(self._value_ ^ other_value) - - def __invert__(self): - if self._inverted_ is None: - if self._boundary_ is KEEP: - # use all bits - self._inverted_ = self.__class__(~self._value_) - else: - # calculate flags not in this member - self._inverted_ = self.__class__(self._flag_mask_ ^ self._value_) - self._inverted_._inverted_ = self - return self._inverted_ - - __ror__ = __or__ - __rand__ = __and__ - __rxor__ = __xor__ - - - -class IntFlag(int, ReprEnum, Flag): - """Support for integer-based Flags""" - - _boundary_ = EJECT - - -def _high_bit(value): - """returns index of highest bit, or -1 if value is zero or negative""" - return value.bit_length() - 1 - -def global_enum_repr(self): - """ - use module.enum_name instead of class.enum_name - - the module is the last module in case of a multi-module name - """ - module = self.__class__.__module__.split('.')[-1] - return '%s.%s' % (module, self._name_) - -def global_flag_repr(self): - """ - use module.flag_name instead of class.flag_name - - the module is the last module in case of a multi-module name - """ - module = self.__class__.__module__.split('.')[-1] - cls_name = self.__class__.__name__ - if self._name_ is None: - return "%s.%s(%r)" % (module, cls_name, self._value_) - if _is_single_bit(self): - return '%s.%s' % (module, self._name_) - if self._boundary_ is not FlagBoundary.KEEP: - return '|'.join(['%s.%s' % (module, name) for name in self.name.split('|')]) - else: - name = [] - for n in self._name_.split('|'): - if n[0].isdigit(): - name.append(n) - else: - name.append('%s.%s' % (module, n)) - return '|'.join(name) - -def global_str(self): - """ - use enum_name instead of class.enum_name - """ - if self._name_ is None: - return "%s(%r)" % (cls_name, self._value_) - else: - return self._name_ - -def global_enum(cls, update_str=False): - """ - decorator that makes the repr() of an enum member reference its module - instead of its class; also exports all members to the enum's module's - global namespace - """ - if issubclass(cls, Flag): - cls.__repr__ = global_flag_repr - else: - cls.__repr__ = global_enum_repr - if not issubclass(cls, ReprEnum) or update_str: - cls.__str__ = global_str - _sys.modules[cls.__module__].__dict__.update(cls.__members__) - return cls - - -class module(object): - - def __init__(self, cls, *args): - self.__name__ = cls.__name__ - self._parent_module = cls.__module__ - self.__all__ = [] - all_objects = cls.__dict__ - if not args: - args = [k for k, v in all_objects.items() if isinstance(v, (NamedConstant, Enum))] - for name in args: - self.__dict__[name] = all_objects[name] - self.__all__.append(name) - - def register(self): - _sys.modules["%s.%s" % (self._parent_module, self.__name__)] = self - -if StdlibEnumMeta: - - from _weakrefset import WeakSet - - def __subclasscheck__(cls, subclass): - """ - Override for issubclass(subclass, cls). - """ - if not isinstance(subclass, type): - raise TypeError('issubclass() arg 1 must be a class (got %r)' % (subclass, )) - # Check cache - try: - cls.__dict__['_subclass_cache_'] - except KeyError: - cls._subclass_cache_ = WeakSet() - cls._subclass_negative_cache_ = WeakSet() - except RecursionError: - import sys - exc, cls, tb = sys.exc_info() - exc = RecursionError('possible causes for endless recursion:\n - __getattribute__ is not ignoring __dunder__ attibutes\n - __instancecheck__ and/or __subclasscheck_ are (mutually) recursive\n see `aenum.remove_stdlib_integration` for temporary work-around') - raise_from_none(exc) - if subclass in cls._subclass_cache_: - return True - # Check negative cache - elif subclass in cls._subclass_negative_cache_: - return False - if cls is subclass: - cls._subclass_cache_.add(subclass) - return True - # Check if it's a direct subclass - if cls in getattr(subclass, '__mro__', ()): - cls._subclass_cache_.add(subclass) - return True - # Check if it's an aenum.Enum|IntEnum|IntFlag|Flag subclass - if cls is StdlibIntFlag and issubclass(subclass, IntFlag): - cls._subclass_cache_.add(subclass) - return True - elif cls is StdlibFlag and issubclass(subclass, Flag): - cls._subclass_cache_.add(subclass) - return True - elif cls is StdlibIntEnum and issubclass(subclass, IntEnum): - cls._subclass_cache_.add(subclass) - return True - if cls is StdlibEnum and issubclass(subclass, Enum): - cls._subclass_cache_.add(subclass) - return True - # No dice; update negative cache - cls._subclass_negative_cache_.add(subclass) - return False - - def __instancecheck__(cls, instance): - subclass = instance.__class__ - try: - return cls.__subclasscheck__(subclass) - except RecursionError: - import sys - exc, cls, tb = sys.exc_info() - exc = RecursionError('possible causes for endless recursion:\n - __getattribute__ is not ignoring __dunder__ attibutes\n - __instancecheck__ and/or __subclasscheck_ are (mutually) recursive\n see `aenum.remove_stdlib_integration` for temporary work-around') - raise_from_none(exc) - - StdlibEnumMeta.__subclasscheck__ = __subclasscheck__ - StdlibEnumMeta.__instancecheck__ = __instancecheck__ - -def add_stdlib_integration(): - if StdlibEnum: - StdlibEnumMeta.__subclasscheck__ = __subclasscheck__ - StdlibEnumMeta.__instancecheck__ = __instancecheck__ - -def remove_stdlib_integration(): - """ - Remove the __instancecheck__ and __subclasscheck__ overrides from the stdlib Enum. - - Those overrides are in place so that code detecting stdlib enums will also detect - aenum enums. If a buggy __getattribute__, __instancecheck__, or __subclasscheck__ - is defined on a custom EnumMeta then RecursionErrors can result; using this - function after importing aenum will solve that problem, but the better solution is - to fix the buggy method. - """ - if StdlibEnum: - del StdlibEnumMeta.__instancecheck__ - del StdlibEnumMeta.__subclasscheck__ - diff --git a/venv/Lib/site-packages/aenum/__pycache__/__init__.cpython-39.pyc b/venv/Lib/site-packages/aenum/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 176f78e250e4099e3c3b3bd6e90f89417d332065..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 104964 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 - ----------------- - -An ``Enum`` is a set of symbolic names (members) bound to unique, constant -values. Within an enumeration, the members can be compared by identity, and -the enumeration itself can be iterated over. - -A ``NamedTuple`` is a class-based, fixed-length tuple with a name for each -possible position accessible using attribute-access notation. - -A ``NamedConstant`` is a class whose members cannot be rebound; it lacks all -other ``Enum`` capabilities, however; consequently, it can have duplicate -values. There is also a ``module`` function that can insert the -``NamedConstant`` class into ``sys.modules`` where it will appear to be a -module whose top-level names cannot be rebound. - -.. note:: - ``constant`` refers to names not being rebound; mutable objects can be - mutated. - - -Module Contents ---------------- - -This module defines five enumeration classes that can be used to define unique -sets of names and values, one ``Enum`` class decorator, one ``NamedTuple`` -class, one ``NamedConstant`` class, and several helpers. - -``NamedConstant`` - -NamedConstant class for creating groups of constants. These names cannot be -rebound to other values. - -``Enum`` - -Base class for creating enumerated constants. See section `Enum Functional API`_ -for an alternate construction syntax. - -``AddValue`` - -Flag specifying that ``_generate_next_value_`` should always be called to -provide the initial value for an enum member. - -``MultiValue`` - -Flag specifying that each item of tuple value is a separate value for that -member; the first tuple item is the canonical one. - -``NoAlias`` - -Flag specifying that duplicate valued members are distinct and not aliases; -by-value lookups are disabled. - -``Unique`` - -Flag specifying that duplicate valued members are not allowed. - -.. note:: - The flags are inherited by the enumeration's subclasses. To use them in - Python 2 assign to ``_settings_`` in the class body. - -``IntEnum`` - -Base class for creating enumerated constants that are also subclasses of ``int``. - -``AutoNumberEnum`` - -Derived class that automatically assigns an ``int`` value to each member. - -``OrderedEnum`` - -Derived class that adds ``<``, ``<=``, ``>=``, and ``>`` methods to an ``Enum``. - -``UniqueEnum`` - -Derived class that ensures only one name is bound to any one value. - -``unique`` - -Enum class decorator that ensures only one name is bound to any one value. - -.. note:: - - the ``UniqueEnum`` class, the ``unique`` decorator, and the Unique - flag all do the same thing; you do not need to use more than one of - them at the same time. - -``NamedTuple`` - -Base class for `creating NamedTuples`_, either by subclassing or via it's -functional API. - -``constant`` - -Descriptor to add constant values to an ``Enum``, or advanced constants to -``NamedConstant``. - -``convert`` - -Helper to transform target global variables into an ``Enum``. - -``enum`` - -Helper for specifying keyword arguments when creating ``Enum`` members. - -``export`` - -Helper for inserting ``Enum`` members and ``NamedConstant`` constants into a -namespace (usually ``globals()``. - -``extend_enum`` - -Helper for adding new ``Enum`` members, both stdlib and aenum. - -``module`` - -Function to take a ``NamedConstant`` or ``Enum`` class and insert it into -``sys.modules`` with the affect of a module whose top-level constant and -member names cannot be rebound. - -``skip`` - -Descriptor to add a normal (non-``Enum`` member) attribute to an ``Enum`` -or ``NamedConstant``. - - -Creating an Enum ----------------- - -Enumerations are created using the ``class`` syntax, which makes them -easy to read and write. An alternative creation method is described in -`Enum Functional API`_. To define an enumeration, subclass ``Enum`` as -follows:: - - >>> from aenum import Enum - >>> class Color(Enum): - ... red = 1 - ... green = 2 - ... blue = 3 - -*Nomenclature* - - - The class ``Color`` is an *enumeration* (or *enum*) - - The attributes ``Color.red``, ``Color.green``, etc., are - *enumeration members* (or *enum members*). - - The enum members have *names* and *values* (the name of - ``Color.red`` is ``red``, the value of ``Color.blue`` is - ``3``, etc.) - -.. note:: - - Even though we use the ``class`` syntax to create Enums, Enums - are not normal Python classes. See `How are Enums different?`_ for - more details. - -Enumeration members have human readable string representations:: - - >>> print(Color.red) - Color.red - -...while their ``repr`` has more information:: - - >>> print(repr(Color.red)) - - -The *type* of an enumeration member is the enumeration it belongs to:: - - >>> type(Color.red) - - >>> isinstance(Color.green, Color) - True - -Enumerations support iteration. In Python 3.x definition order is used; in -Python 2.x the definition order is not available, but class attribute -``_order_`` is supported; otherwise, value order is used if posible, -otherwise alphabetical name order is used:: - - >>> class Shake(Enum): - ... _order_ = 'vanilla chocolate cookies mint' # only needed in 2.x - ... vanilla = 7 - ... chocolate = 4 - ... cookies = 9 - ... mint = 3 - ... - >>> for shake in Shake: - ... print(shake) - ... - Shake.vanilla - Shake.chocolate - Shake.cookies - Shake.mint - -The ``_order_`` attribute is always removed, but in 3.x it is also used to -verify that definition order is the same (useful for py2&3 code bases); -however, in the stdlib version it will be ignored and not removed. - -.. note:: - - To maintain compatibility with Python 3.4 and 3.5, use __order__ - instead (double leading and trailing underscores). - -Enumeration members are hashable, so they can be used in dictionaries and sets:: - - >>> apples = {} - >>> apples[Color.red] = 'red delicious' - >>> apples[Color.green] = 'granny smith' - >>> apples == {Color.red: 'red delicious', Color.green: 'granny smith'} - True - -In Python 3 the class syntax has a few extra advancements:: - - --> class Color( - ... Enum, - ... settings=(AddValue, MultiValue, NoAlias, Unique), - ... init='field_name1 field_name2 ...', - ... start=7, - ... ) - ... - -``start`` is used to specify the starting value for the first member:: - - --> class Count(Enum, start=11): - ... eleven - ... twelve - ... - --> Count.twelve.value == 12 - True - -``init`` specifies the attribute names to store creation values to:: - - --> class Planet(Enum, init='mass radius'): - ... MERCURY = (3.303e+23, 2.4397e6) - ... EARTH = (5.976e+24, 6.37814e6) - ... - --> Planet.EARTH.value - (5.976e+24, 6378140.0) - --> Planet.EARTH.radius - 2.4397e6 - -The various settings enable special behavior: - -- ``AddValue`` calls a user supplied ``_generate_next_value_`` to provide - the initial value -- ``MultiValue`` allows multiple values per member instead of the usual 1 -- ``NoAlias`` allows different members to have the same value -- ``Unique`` disallows different members to have the same value - -.. note:: - - To use these features in Python 2 use the _sundered_ versions of - the names in the class body: ``_start_``, ``_init_``, ``_settings_``. - - -Programmatic access to enumeration members and their attributes ---------------------------------------------------------------- - -Sometimes it's useful to access members in enumerations programmatically (i.e. -situations where ``Color.red`` won't do because the exact color is not known -at program-writing time). ``Enum`` allows such access:: - - >>> Color(1) - - >>> Color(3) - - -If you want to access enum members by *name*, use item access:: - - >>> Color['red'] - - >>> Color['green'] - - -If have an enum member and need its ``name`` or ``value``:: - - >>> member = Color.red - >>> member.name - 'red' - >>> member.value - 1 - - -Duplicating enum members and values ------------------------------------ - -Having two enum members (or any other attribute) with the same name is invalid; -in Python 3.x this would raise an error, but in Python 2.x the second member -simply overwrites the first:: - - # python 2.x - --> class Shape(Enum): - ... square = 2 - ... square = 3 - ... - --> Shape.square - - - # python 3.x - --> class Shape(Enum): - ... square = 2 - ... square = 3 - Traceback (most recent call last): - ... - TypeError: Attempted to reuse key: 'square' - -However, two enum members are allowed to have the same value. Given two members -A and B with the same value (and A defined first), B is an alias to A. By-value -lookup of the value of A and B will return A. By-name lookup of B will also -return A:: - - >>> class Shape(Enum): - ... _order_ = 'square diamond circle' # needed in 2.x - ... square = 2 - ... diamond = 1 - ... circle = 3 - ... alias_for_square = 2 - ... - >>> Shape.square - - >>> Shape.alias_for_square - - >>> Shape(2) - - - -Allowing aliases is not always desirable. ``unique`` can be used to ensure -that none exist in a particular enumeration:: - - >>> from aenum import unique - >>> @unique - ... class Mistake(Enum): - ... _order_ = 'one two three' # only needed in 2.x - ... one = 1 - ... two = 2 - ... three = 3 - ... four = 3 - Traceback (most recent call last): - ... - ValueError: duplicate names found in : four -> three - -Iterating over the members of an enum does not provide the aliases:: - - >>> list(Shape) - [, , ] - -The special attribute ``__members__`` is a dictionary mapping names to members. -It includes all names defined in the enumeration, including the aliases:: - - >>> for name, member in sorted(Shape.__members__.items()): - ... name, member - ... - ('alias_for_square', ) - ('circle', ) - ('diamond', ) - ('square', ) - -The ``__members__`` attribute can be used for detailed programmatic access to -the enumeration members. For example, finding all the aliases:: - - >>> [n for n, mbr in Shape.__members__.items() if mbr.name != n] - ['alias_for_square'] - -Comparisons ------------ - -Enumeration members are compared by identity:: - - >>> Color.red is Color.red - True - >>> Color.red is Color.blue - False - >>> Color.red is not Color.blue - True - -Ordered comparisons between enumeration values are *not* supported. Enum -members are not integers (but see `IntEnum`_ below):: - - >>> Color.red < Color.blue - Traceback (most recent call last): - File "", line 1, in - TypeError: unorderable types: Color() < Color() - -.. warning:: - - In Python 2 *everything* is ordered, even though the ordering may not - make sense. If you want your enumerations to have a sensible ordering - consider using an `OrderedEnum`_. - - -Equality comparisons are defined though:: - - >>> Color.blue == Color.red - False - >>> Color.blue != Color.red - True - >>> Color.blue == Color.blue - True - -Comparisons against non-enumeration values will always compare not equal -(again, ``IntEnum`` was explicitly designed to behave differently, see -below):: - - >>> Color.blue == 2 - False - - -Allowed members and attributes of enumerations ----------------------------------------------- - -The examples above use integers for enumeration values. Using integers is -short and handy (and provided by default by the `Enum Functional API`_), but not -strictly enforced. In the vast majority of use-cases, one doesn't care what -the actual value of an enumeration is. But if the value *is* important, -enumerations can have arbitrary values. - -Enumerations are Python classes, and can have methods and special methods as -usual. If we have this enumeration:: - - >>> class Mood(Enum): - ... funky = 1 - ... happy = 3 - ... - ... def describe(self): - ... # self is the member here - ... return self.name, self.value - ... - ... def __str__(self): - ... return 'my custom str! {0}'.format(self.value) - ... - ... @classmethod - ... def favorite_mood(cls): - ... # cls here is the enumeration - ... return cls.happy - -Then:: - - >>> Mood.favorite_mood() - - >>> Mood.happy.describe() - ('happy', 3) - >>> str(Mood.funky) - 'my custom str! 1' - -The rules for what is allowed are as follows: _sunder_ names (starting and -ending with a single underscore) are reserved by enum and cannot be used; -all other attributes defined within an enumeration will become members of this -enumeration, with the exception of *__dunder__* names and descriptors (methods -are also descriptors). - -.. note:: - - If your enumeration defines ``__new__`` and/or ``__init__`` then - whatever value(s) were given to the enum member will be passed into - those methods. See `Planet`_ for an example. - - -Restricted Enum subclassing ---------------------------- - -A new `Enum` class must have one base Enum class, up to one concrete -data type, and as many `object`-based mixin classes as needed. The -order of these base classes is:: - - def EnumName([mix-in, ...,] [data-type,] base-enum): - pass - -Also, subclassing an enumeration is allowed only if the enumeration does not define - -any members. So this is forbidden:: - - >>> class MoreColor(Color): - ... pink = 17 - Traceback (most recent call last): - ... - TypeError: cannot extend - -But this is allowed:: - - >>> class Foo(Enum): - ... def some_behavior(self): - ... pass - ... - >>> class Bar(Foo): - ... happy = 1 - ... sad = 2 - ... - -Allowing subclassing of enums that define members would lead to a violation of -some important invariants of types and instances. On the other hand, it makes -sense to allow sharing some common behavior between a group of enumerations. -(See `OrderedEnum`_ for an example.) - - -Pickling --------- - -Enumerations can be pickled and unpickled:: - - >>> from aenum.test import Fruit - >>> from pickle import dumps, loads - >>> Fruit.tomato is loads(dumps(Fruit.tomato, 2)) - True - -The usual restrictions for pickling apply: picklable enums must be defined in -the top level of a module, since unpickling requires them to be importable -from that module. - -.. note:: - - With pickle protocol version 4 (introduced in Python 3.4) it is possible - to easily pickle enums nested in other classes. - - - -Enum Functional API -------------------- - -The ``Enum`` class is callable, providing the following functional API:: - - >>> Animal = Enum('Animal', 'ant bee cat dog') - >>> Animal - - >>> Animal.ant - - >>> Animal.ant.value - 1 - >>> list(Animal) - [, , , ] - -The semantics of this API resemble ``namedtuple``. The first argument -of the call to ``Enum`` is the name of the enumeration. - -The second argument is the *source* of enumeration member names. It can be a -whitespace-separated string of names, a sequence of names, a sequence of -2-tuples with key/value pairs, or a mapping (e.g. dictionary) of names to -values. The last two options enable assigning arbitrary values to -enumerations; the others auto-assign increasing integers starting with 1. A -new class derived from ``Enum`` is returned. In other words, the above -assignment to ``Animal`` is equivalent to:: - - >>> class Animals(Enum): - ... ant = 1 - ... bee = 2 - ... cat = 3 - ... dog = 4 - -Pickling enums created with the functional API can be tricky as frame stack -implementation details are used to try and figure out which module the -enumeration is being created in (e.g. it will fail if you use a utility -function in separate module, and also may not work on IronPython or Jython). -The solution is to specify the module name explicitly as follows:: - - >>> Animals = Enum('Animals', 'ant bee cat dog', module=__name__) - -Derived Enumerations --------------------- - -IntEnum -^^^^^^^ - -A variation of ``Enum`` is provided which is also a subclass of -``int``. Members of an ``IntEnum`` can be compared to integers; -by extension, integer enumerations of different types can also be compared -to each other:: - - >>> from aenum import IntEnum - >>> class Shape(IntEnum): - ... circle = 1 - ... square = 2 - ... - >>> class Request(IntEnum): - ... post = 1 - ... get = 2 - ... - >>> Shape == 1 - False - >>> Shape.circle == 1 - True - >>> Shape.circle == Request.post - True - -However, they still can't be compared to standard ``Enum`` enumerations:: - - >>> class Shape(IntEnum): - ... circle = 1 - ... square = 2 - ... - >>> class Color(Enum): - ... red = 1 - ... green = 2 - ... - >>> Shape.circle == Color.red - False - -``IntEnum`` values behave like integers in other ways you'd expect:: - - >>> int(Shape.circle) - 1 - >>> ['a', 'b', 'c'][Shape.circle] - 'b' - >>> [i for i in range(Shape.square)] - [0, 1] - -For the vast majority of code, ``Enum`` is strongly recommended, -since ``IntEnum`` breaks some semantic promises of an enumeration (by -being comparable to integers, and thus by transitivity to other -unrelated enumerations). It should be used only in special cases where -there's no other choice; for example, when integer constants are -replaced with enumerations and backwards compatibility is required with code -that still expects integers. - - -IntFlag -^^^^^^^ - -The next variation of ``Enum`` provided, ``IntFlag``, is also based -on ``int``. The difference being ``IntFlag`` members can be combined -using the bitwise operators (&, \|, ^, ~) and the result is still an -``IntFlag`` member. However, as the name implies, ``IntFlag`` -members also subclass ``int`` and can be used wherever an ``int`` is -used. Any operation on an ``IntFlag`` member besides the bit-wise -operations will lose the ``IntFlag`` membership. - -Sample ``IntFlag`` class:: - - >>> from aenum import IntFlag - >>> class Perm(IntFlag): - ... _order_ = 'R W X' - ... R = 4 - ... W = 2 - ... X = 1 - ... - >>> Perm.R | Perm.W - - >>> Perm.R + Perm.W - 6 - >>> RW = Perm.R | Perm.W - >>> Perm.R in RW - True - -It is also possible to name the combinations:: - - >>> class Perm(IntFlag): - ... _order_ = 'R W X' - ... R = 4 - ... W = 2 - ... X = 1 - ... RWX = 7 - >>> Perm.RWX - - >>> ~Perm.RWX - - -Another important difference between ``IntFlag`` and ``Enum`` is that -if no flags are set (the value is 0), its boolean evaluation is ``False``:: - - >>> Perm.R & Perm.X - - >>> bool(Perm.R & Perm.X) - False - -Because ``IntFlag`` members are also subclasses of ``int`` they can -be combined with them:: - - >>> Perm.X | 4 - - -If the result is not a ``Flag`` then, depending on the ``_boundary_`` setting, -an exception is raised (``STRICT``), the extra bits are lost (``CONFORM``), or -it reverts to an int (``EJECT``): - - >>> from aenum import STRICT, CONFORM, EJECT - >>> Perm._boundary_ = STRICT - >>> Perm.X | 8 - Traceback (most recent call last): - ... - ValueError: Perm: invalid value: 9 - given 0b0 1001 - allowed 0b0 0111 - - >>> Perm._boundary_ = EJECT - >>> Perm.X | 8 - 9 - - >>> Perm._boundary_ = CONFORM - >>> Perm.X | 8 - - - -Flag -^^^^ - -The last variation is ``Flag``. Like ``IntFlag``, ``Flag`` -members can be combined using the bitwise operators (&, \|, ^, ~). Unlike -``IntFlag``, they cannot be combined with, nor compared against, any -other ``Flag`` enumeration, nor ``int``. While it is possible to -specify the values directly it is recommended to use ``auto`` as the -value and let ``Flag`` select an appropriate value. - -Like ``IntFlag``, if a combination of ``Flag`` members results in no -flags being set, the boolean evaluation is ``False``:: - - >>> from aenum import Flag, auto - >>> class Color(Flag): - ... RED = auto() - ... BLUE = auto() - ... GREEN = auto() - ... - >>> Color.RED & Color.GREEN - - >>> bool(Color.RED & Color.GREEN) - False - -Individual flags should have values that are powers of two (1, 2, 4, 8, ...), -while combinations of flags won't:: - - --> class Color(Flag): - ... RED = auto() - ... BLUE = auto() - ... GREEN = auto() - ... WHITE = RED | BLUE | GREEN - ... - --> Color.WHITE - - -Giving a name to the "no flags set" condition does not change its boolean -value:: - - >>> class Color(Flag): - ... BLACK = 0 - ... RED = auto() - ... BLUE = auto() - ... GREEN = auto() - ... - >>> Color.BLACK - - >>> bool(Color.BLACK) - False - -Flags can be iterated over to retrieve the individual truthy flags in the value:: - - >>> class Color(Flag): - ... _order_ = 'BLACK RED BLUE GREEN WHITE' - ... BLACK = 0 - ... RED = auto() - ... BLUE = auto() - ... GREEN = auto() - ... WHITE = RED | BLUE | GREEN - ... - >>> list(Color.GREEN) - [] - >>> list(Color.WHITE) - [, , ] - -.. note:: - - For the majority of new code, ``Enum`` and ``Flag`` are strongly - recommended, since ``IntEnum`` and ``IntFlag`` break some - semantic promises of an enumeration (by being comparable to integers, and - thus by transitivity to other unrelated enumerations). ``IntEnum`` - and ``IntFlag`` should be used only in cases where ``Enum`` and - ``Flag`` will not do; for example, when integer constants are replaced - with enumerations, or for interoperability with other systems. - - -Others -^^^^^^ - -While ``IntEnum`` is part of the ``aenum`` module, it would be very -simple to implement independently:: - - class MyIntEnum(int, Enum): - pass - -This demonstrates how similar derived enumerations can be defined; for example -a ``MyStrEnum`` that mixes in ``str`` instead of ``int``. - -Some rules: - -1. When subclassing ``Enum``, mix-in types must appear before - ``Enum`` itself in the sequence of bases, as in the ``MyIntEnum`` - example above. -2. While ``Enum`` can have members of any type, once you mix in an - additional type, all the members must have values of that type or be - convertible into that type. This restriction does not apply to mix-ins - which only add methods and don't specify another data type. -3. When another data type is mixed in, the ``value`` attribute is *not the - same* as the enum member itself, although it is equivalant and will compare - equal. -4. %-style formatting: ``%s`` and ``%r`` call ``Enum``'s ``__str__`` and - ``__repr__`` respectively; other codes (such as ``%i`` or ``%h`` for - MyIntEnum) treat the enum member as its mixed-in type. -5. ``str.__format__`` (or ``format``) will use the mixed-in - type's ``__format__``. If the ``Enum``'s ``str`` or ``repr`` is desired - use the ``!s`` or ``!r`` ``str`` format codes. - -.. note:: - - If you override the ``__str__`` method, then it will be used to provide the - string portion of the ``format()`` call. - -.. note:: - - Prior to Python 3.4 there is a bug in ``str``'s %-formatting: ``int`` - subclasses are printed as strings and not numbers when the ``%d``, ``%i``, - or ``%u`` codes are used. - - -Extra Goodies -------------- - -aenum supports a few extra techniques not found in the stdlib version. - -enum -^^^^ - -If you have several items to initialize your ``Enum`` members with and -would like to use keyword arguments, the ``enum`` helper is for you:: - - >>> from aenum import enum - >>> class Presidents(Enum): - ... Washington = enum('George Washington', circa=1776, death=1797) - ... Jackson = enum('Andrew Jackson', circa=1830, death=1837) - ... Lincoln = enum('Abraham Lincoln', circa=1860, death=1865) - ... - >>> Presidents.Lincoln - - -extend_enum -^^^^^^^^^^^ - -For those rare cases when you need to create your ``Enum`` in pieces, you -can use ``extend_enum`` to add new members after the initial creation -(the new member is returned):: - - >>> from aenum import extend_enum - >>> class Color(Enum): - ... red = 1 - ... green = 2 - ... blue = 3 - ... - >>> list(Color) - [, , ] - >>> extend_enum(Color, 'opacity', 4) - - >>> list(Color) - [, , , ] - >>> Color.opacity in Color - True - >>> Color.opacity.name == 'opacity' - True - >>> Color.opacity.value == 4 - True - >>> Color(4) - - >>> Color['opacity'] - - - --> Color.__members__ - OrderedDict([ - ('red', ), - ('green', ), - ('blue', ), - ('opacity', ) - ]) - -constant -^^^^^^^^ - -If you need to have some constant value in your ``Enum`` that isn't a member, -use ``constant``:: - - >>> from aenum import constant - >>> class Planet(Enum): - ... MERCURY = (3.303e+23, 2.4397e6) - ... EARTH = (5.976e+24, 6.37814e6) - ... JUPITER = (1.9e+27, 7.1492e7) - ... URANUS = (8.686e+25, 2.5559e7) - ... G = constant(6.67300E-11) - ... def __init__(self, mass, radius): - ... self.mass = mass # in kilograms - ... self.radius = radius # in meters - ... @property - ... def surface_gravity(self): - ... # universal gravitational constant (m3 kg-1 s-2) - ... return self.G * self.mass / (self.radius * self.radius) - ... - >>> Planet.EARTH.value - (5.976e+24, 6378140.0) - >>> Planet.EARTH.surface_gravity - 9.802652743337129 - >>> Planet.G - 6.673e-11 - >>> Planet.G = 9 - Traceback (most recent call last): - ... - AttributeError: Planet: cannot rebind constant 'G' - -skip -^^^^ - -If you need a standard attribute that is not converted into an ``Enum`` -member, use ``skip``:: - - >>> from aenum import skip - >>> class Color(Enum): - ... red = 1 - ... green = 2 - ... blue = 3 - ... opacity = skip(0.45) - ... - >>> Color.opacity - 0.45 - >>> Color.opacity = 0.77 - >>> Color.opacity - 0.77 - -start -^^^^^ - -``start`` can be used to turn on auto-numbering (useful for when you don't -care which numbers are assigned as long as they are consistent and in order) -The Python 3 version can look like this:: - - >>> class Color(Enum, start=1): # doctest: +SKIP - ... red, green, blue - ... - >>> Color.blue - - -This can also be done in Python 2, albeit not as elegantly (this also works in -Python 3):: - - >>> class Color(Enum): # doctest: +SKIP - ... _start_ = 1 - ... red = auto() - ... green = auto() - ... blue = auto() - ... - >>> Color.blue - - -init -^^^^ - -If you need an ``__init__`` method that does nothing besides save its -arguments, ``init`` is for you:: - - >>> class Planet(Enum, init='mass radius'): # doctest: +SKIP - ... MERCURY = (3.303e+23, 2.4397e6) - ... EARTH = (5.976e+24, 6.37814e6) - ... JUPITER = (1.9e+27, 7.1492e7) - ... URANUS = (8.686e+25, 2.5559e7) - ... G = constant(6.67300E-11) - ... @property - ... def surface_gravity(self): - ... # universal gravitational constant (m3 kg-1 s-2) - ... return self.G * self.mass / (self.radius * self.radius) - ... - >>> Planet.JUPITER.value - (1.9e+27, 71492000.0) - >>> Planet.JUPITER.mass - 1.9e+27 - -.. note:: - - Just as with ``start`` above, in Python 2 you must put the keyword as a - _sunder_ in the class body -- ``_init_ = 'mass radius'``. - -init and missing values -^^^^^^^^^^^^^^^^^^^^^^^ - -If ``_init_`` calls for values that are not supplied, ``_generate_next_value_`` -will be called in an effort to generate them. Here is an example in Python 2:: - - >>> from aenum import Enum - >>> class SelectionEnum(Enum): - ... _init_ = 'db user' - ... def __new__(cls, *args, **kwds): - ... count = len(cls.__members__) - ... obj = object.__new__(cls) - ... obj._count = count - ... obj._value_ = args - ... return obj - ... @staticmethod - ... def _generate_next_value_(name, start, count, values, *args, **kwds): - ... return (name, ) + args - ... - >>> class NotificationType(SelectionEnum): - ... # usually, name is the same as db - ... # but not for blanks - ... blank = '', '' - ... C = 'Catalog' - ... S = 'Sheet' - ... B = 'Both' - ... - >>> NotificationType.blank - - >>> NotificationType.B - - >>> NotificationType.B.db - 'B' - >>> NotificationType.B.user - 'Both' - -combining Flag with other data types -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Flag does support being combined with other data types. To support this you -need to provide a ``_create_pseudo_member_values_`` method which will be called -with the members in a composite flag. You may also need to provide a custom -``__new__`` method:: - - >>> class AnsiFlag(str, Flag): - ... def __new__(cls, value, code): - ... str_value = '\x1b[%sm' % code - ... obj = str.__new__(cls, str_value) - ... obj._value_ = value - ... obj.code = code - ... return obj - ... @classmethod - ... def _create_pseudo_member_values_(cls, members, *values): - ... code = ';'.join(m.code for m in members) - ... return values + (code, ) - ... _order_ = 'FG_Red FG_Green BG_Magenta BG_White' - ... FG_Red = '31' # ESC [ 31 m # red - ... FG_Green = '32' # ESC [ 32 m # green - ... BG_Magenta = '45' # ESC [ 35 m # magenta - ... BG_White = '47' # ESC [ 37 m # white - ... - >>> color = AnsiFlag.BG_White | AnsiFlag.FG_Red - >>> repr(color) - '' - >>> str.__repr__(color) - "'\\x1b[31;47m'" - -.. note:: - - If you do not provide your own ``_create_pseudo_member_values_`` the flags - may still combine, but may be missing functionality. - - -Decorators ----------- - -unique -^^^^^^ - -A ``class`` decorator specifically for enumerations. It searches an -enumeration's ``__members__`` gathering any aliases it finds; if any are -found ``ValueError`` is raised with the details:: - - >>> @unique - ... class NoDupes(Enum): - ... first = 'one' - ... second = 'two' - ... third = 'two' - Traceback (most recent call last): - ... - ValueError: duplicate names found in : third -> second - - -Interesting examples --------------------- - -While ``Enum`` and ``IntEnum`` are expected to cover the majority of -use-cases, they cannot cover them all. Here are recipes for some different -types of enumerations that can be used directly (the first three are included -in the module), or as examples for creating one's own. - - -AutoNumber -^^^^^^^^^^ - -Avoids having to specify the value for each enumeration member:: - - >>> class AutoNumber(Enum): - ... def __new__(cls): - ... value = len(cls.__members__) + 1 - ... obj = object.__new__(cls) - ... obj._value_ = value - ... return obj - ... - >>> class Color(AutoNumber): - ... _order_ = "red green blue" # only needed in 2.x - ... red = () - ... green = () - ... blue = () - ... - >>> Color.green.value == 2 - True - -.. note:: - - The `__new__` method, if defined, is used during creation of the Enum - members; it is then replaced by Enum's `__new__` which is used after - class creation for lookup of existing members. Due to the way Enums are - supposed to behave, there is no way to customize Enum's `__new__` without - modifying the class after it is created. - - -UniqueEnum -^^^^^^^^^^ - -Raises an error if a duplicate member name is found instead of creating an -alias:: - - >>> class UniqueEnum(Enum): - ... def __init__(self, *args): - ... cls = self.__class__ - ... if any(self.value == e.value for e in cls): - ... a = self.name - ... e = cls(self.value).name - ... raise ValueError( - ... "aliases not allowed in UniqueEnum: %r --> %r" - ... % (a, e)) - ... - >>> class Color(UniqueEnum): - ... _order_ = 'red green blue' - ... red = 1 - ... green = 2 - ... blue = 3 - ... grene = 2 - Traceback (most recent call last): - ... - ValueError: aliases not allowed in UniqueEnum: 'grene' --> 'green' - - -OrderedEnum -^^^^^^^^^^^ - -An ordered enumeration that is not based on ``IntEnum`` and so maintains -the normal ``Enum`` invariants (such as not being comparable to other -enumerations):: - - >>> class OrderedEnum(Enum): - ... def __ge__(self, other): - ... if self.__class__ is other.__class__: - ... return self._value_ >= other._value_ - ... return NotImplemented - ... def __gt__(self, other): - ... if self.__class__ is other.__class__: - ... return self._value_ > other._value_ - ... return NotImplemented - ... def __le__(self, other): - ... if self.__class__ is other.__class__: - ... return self._value_ <= other._value_ - ... return NotImplemented - ... def __lt__(self, other): - ... if self.__class__ is other.__class__: - ... return self._value_ < other._value_ - ... return NotImplemented - ... - >>> class Grade(OrderedEnum): - ... __ordered__ = 'A B C D F' - ... A = 5 - ... B = 4 - ... C = 3 - ... D = 2 - ... F = 1 - ... - >>> Grade.C < Grade.A - True - - -Planet -^^^^^^ - -If ``__new__`` or ``__init__`` is defined the value of the enum member -will be passed to those methods:: - - >>> class Planet(Enum): - ... MERCURY = (3.303e+23, 2.4397e6) - ... VENUS = (4.869e+24, 6.0518e6) - ... EARTH = (5.976e+24, 6.37814e6) - ... MARS = (6.421e+23, 3.3972e6) - ... JUPITER = (1.9e+27, 7.1492e7) - ... SATURN = (5.688e+26, 6.0268e7) - ... URANUS = (8.686e+25, 2.5559e7) - ... NEPTUNE = (1.024e+26, 2.4746e7) - ... def __init__(self, mass, radius): - ... self.mass = mass # in kilograms - ... self.radius = radius # in meters - ... @property - ... def surface_gravity(self): - ... # universal gravitational constant (m3 kg-1 s-2) - ... G = 6.67300E-11 - ... return G * self.mass / (self.radius * self.radius) - ... - >>> Planet.EARTH.value - (5.976e+24, 6378140.0) - >>> Planet.EARTH.surface_gravity - 9.802652743337129 - - -How are Enums different? ------------------------- - -Enums have a custom metaclass that affects many aspects of both derived Enum -classes and their instances (members). - - -Enum Classes -^^^^^^^^^^^^ - -The ``EnumMeta`` metaclass is responsible for providing the -``__contains__``, ``__dir__``, ``__iter__`` and other methods that -allow one to do things with an ``Enum`` class that fail on a typical -class, such as ``list(Color)`` or ``some_var in Color``. ``EnumMeta`` is -responsible for ensuring that various other methods on the final ``Enum`` -class are correct (such as ``__new__``, ``__getnewargs__``, -``__str__`` and ``__repr__``). - -.. note:: - - ``__dir__`` is not changed in the Python 2 line as it messes up some - of the decorators included in the stdlib. - - -Enum Members (aka instances) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The most interesting thing about Enum members is that they are singletons. -``EnumMeta`` creates them all while it is creating the ``Enum`` -class itself, and then puts a custom ``__new__`` in place to ensure -that no new ones are ever instantiated by returning only the existing -member instances. - - -Finer Points -^^^^^^^^^^^^ - -``Enum`` members are instances of an ``Enum`` class, but are not -accessible as `EnumClass.member1.member2`. -(changed in version 1.1.1 to be accessible) -(changed in version 2.2.4 to be inaccessible):: - - >>> class FieldTypes(Enum): - ... name = 1 - ... value = 2 - ... size = 3 - ... - >>> FieldTypes.size.value - 3 - >>> FieldTypes.size - - >>> FieldTypes.value.size - Traceback (most recent call last): - ... - AttributeError: member has no attribute 'size' - -The ``__members__`` attribute is only available on the class. - - -``__members__`` is always an ``OrderedDict``, with the order being the -definition order in Python 3.x or the order in ``_order_`` in Python 2.7; -if no ``_order_`` was specified in Python 2.7 then the order of -``__members__`` is either increasing value or alphabetically by name. - -If you give your ``Enum`` subclass extra methods, like the `Planet`_ -class above, those methods will show up in a `dir` of the member, -but not of the class (in Python 3.x):: - - --> dir(Planet) - ['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', - 'VENUS', '__class__', '__doc__', '__members__', '__module__'] - --> dir(Planet.EARTH) - ['__class__', '__doc__', '__module__', 'name', 'surface_gravity', 'value'] - -A ``__new__`` method will only be used for the creation of the -``Enum`` members -- after that it is replaced. This means if you wish to -change how ``Enum`` members are looked up you either have to write a -helper function or a ``classmethod``. - -.. note:: - - If you create your own ``__new__`` you should set the ``_value_`` in it; - if you do not, aenum will try to, but will raise a ``TypeError`` if it - cannot. - -If the stdlib ``enum`` is available (Python 3.4+ and it hasn't been shadowed -by, for example, ``enum34``) then aenum will be a subclass of it. - -To use the ``AddValue``, ``MultiValue``, ``NoAlias``, and ``Unique`` flags -in Py2 or Py2/Py3 codebases, use ``_settings_ = ...`` in the class body. - -To use ``init`` in Py2 or Py2/Py3 codebases use ``_init_`` in the class body. - -To use ``start`` in Py2 or Py2/Py3 codebases use ``_start_`` in the class body. - -When creating class bodies dynamically, put any variables you need to use into -``_ignore_``:: - - >>> from datetime import timedelta - >>> from aenum import NoAlias - >>> class Period(timedelta, Enum): - ... ''' - ... different lengths of time - ... ''' - ... _init_ = 'value period' - ... _settings_ = NoAlias - ... _ignore_ = 'Period i' - ... Period = vars() - ... for i in range(31): - ... Period['day_%d' % i] = i, 'day' - ... for i in range(15): - ... Period['week_%d' % i] = i*7, 'week' - ... - >>> hasattr(Period, '_ignore_') - False - >>> hasattr(Period, 'Period') - False - >>> hasattr(Period, 'i') - False - -The name listed in ``_ignore_``, as well as ``_ignore_`` itself, will not be -present in the final enumeration as neither attributes nor members. - -.. note:: - - except for __dunder__ attributes/methods, all _sunder_ attributes must - be before any thing else in the class body - -.. note:: - - all _sunder_ attributes that affect member creation are only looked up in - the last ``Enum`` class listed in the class header - - -Creating NamedTuples --------------------- - -Simple -^^^^^^ - -The most common way to create a new NamedTuple will be via the functional API:: - - >>> from aenum import NamedTuple - >>> Book = NamedTuple('Book', 'title author genre', module=__name__) - -This creates a ``NamedTuple`` called ``Book`` that will always contain three -items, each of which is also addressable as ``title``, ``author``, or ``genre``. - -``Book`` instances can be created using positional or keyword argements or a -mixture of the two:: - - >>> b1 = Book('Lord of the Rings', 'J.R.R. Tolkien', 'fantasy') - >>> b2 = Book(title='Jhereg', author='Steven Brust', genre='fantasy') - >>> b3 = Book('Empire', 'Orson Scott Card', genre='scifi') - -If too few or too many arguments are used a ``TypeError`` will be raised:: - - >>> b4 = Book('Hidden Empire') - Traceback (most recent call last): - ... - TypeError: values not provided for field(s): author, genre - >>> b5 = Book(genre='business') - Traceback (most recent call last): - ... - TypeError: values not provided for field(s): title, author - -As a ``class`` the above ``Book`` ``NamedTuple`` would look like:: - - >>> class Book(NamedTuple): - ... title = 0 - ... author = 1 - ... genre = 2 - ... - -For compatibility with the stdlib ``namedtuple``, NamedTuple also has the -``_asdict``, ``_make``, and ``_replace`` methods, and the ``_fields`` -attribute, which all function similarly:: - - >>> class Point(NamedTuple): - ... x = 0, 'horizontal coordinate', 1 - ... y = 1, 'vertical coordinate', -1 - ... - >>> class Color(NamedTuple): - ... r = 0, 'red component', 11 - ... g = 1, 'green component', 29 - ... b = 2, 'blue component', 37 - ... - >>> Pixel = NamedTuple('Pixel', Point+Color, module=__name__) - >>> pixel = Pixel(99, -101, 255, 128, 0) - - >>> pixel._asdict() - OrderedDict([('x', 99), ('y', -101), ('r', 255), ('g', 128), ('b', 0)]) - - >>> Point._make((4, 5)) - Point(x=4, y=5) - - >>> purple = Color(127, 0, 127) - >>> mid_gray = purple._replace(g=127) - >>> mid_gray - Color(r=127, g=127, b=127) - - >>> pixel._fields - ['x', 'y', 'r', 'g', 'b'] - - >>> Pixel._fields - ['x', 'y', 'r', 'g', 'b'] - - -Advanced -^^^^^^^^ - -The simple method of creating ``NamedTuples`` requires always specifying all -possible arguments when creating instances; failure to do so will raise -exceptions:: - - >>> class Point(NamedTuple): - ... x = 0 - ... y = 1 - ... - >>> Point() - Traceback (most recent call last): - ... - TypeError: values not provided for field(s): x, y - >>> Point(1) - Traceback (most recent call last): - ... - TypeError: values not provided for field(s): y - >>> Point(y=2) - Traceback (most recent call last): - ... - TypeError: values not provided for field(s): x - -However, it is possible to specify both docstrings and default values when -creating a ``NamedTuple`` using the class method:: - - >>> class Point(NamedTuple): - ... x = 0, 'horizontal coordinate', 0 - ... y = 1, 'vertical coordinate', 0 - ... - >>> Point() - Point(x=0, y=0) - >>> Point(1) - Point(x=1, y=0) - >>> Point(y=2) - Point(x=0, y=2) - -It is also possible to create ``NamedTuples`` that only have named attributes -for certain fields; any fields without names can still be accessed by index:: - - >>> class Person(NamedTuple): - ... fullname = 2 - ... phone = 5 - ... - >>> p = Person('Ethan', 'Furman', 'Ethan Furman', - ... 'ethan at stoneleaf dot us', - ... 'ethan.furman', '999.555.1212') - >>> p - Person('Ethan', 'Furman', 'Ethan Furman', 'ethan at stoneleaf dot us', - 'ethan.furman', '999.555.1212') - >>> p.fullname - 'Ethan Furman' - >>> p.phone - '999.555.1212' - >>> p[0] - 'Ethan' - -In the above example the last named field was also the last field possible; in -those cases where you don't need to have the last possible field named, you can -provide a ``_size_`` of ``TupleSize.minimum`` to declare that more fields are -okay:: - - >>> from aenum import TupleSize - >>> class Person(NamedTuple): - ... _size_ = TupleSize.minimum - ... first = 0 - ... last = 1 - ... - -or, optionally if using Python 3:: - - >>> class Person(NamedTuple, size=TupleSize.minimum): # doctest: +SKIP - ... first = 0 - ... last = 1 - -and in use:: - - >>> Person('Ethan', 'Furman') - Person(first='Ethan', last='Furman') - - >>> Person('Ethan', 'Furman', 'ethan.furman') - Person('Ethan', 'Furman', 'ethan.furman') - - >>> Person('Ethan', 'Furman', 'ethan.furman', 'yay Python!') - Person('Ethan', 'Furman', 'ethan.furman', 'yay Python!') - - >>> Person('Ethan') - Traceback (most recent call last): - ... - TypeError: values not provided for field(s): last - -Also, for those cases where even named fields may not be present, you can -specify ``TupleSize.variable``:: - - >>> class Person(NamedTuple): - ... _size_ = TupleSize.variable - ... first = 0 - ... last = 1 - ... - - >>> Person('Ethan') - Person('Ethan') - - >>> Person(last='Furman') - Traceback (most recent call last): - ... - TypeError: values not provided for field(s): first - -Creating new ``NamedTuples`` from existing ``NamedTuples`` is simple:: - - >>> Point = NamedTuple('Point', 'x y') - >>> Color = NamedTuple('Color', 'r g b') - >>> Pixel = NamedTuple('Pixel', Point+Color, module=__name__) - >>> Pixel - - -The existing fields in the bases classes are renumbered to fit the new class, -but keep their doc strings and default values. If you use standard -subclassing:: - - >>> Point = NamedTuple('Point', 'x y') - >>> class Pixel(Point): - ... r = 2, 'red component', 11 - ... g = 3, 'green component', 29 - ... b = 4, 'blue component', 37 - ... - >>> Pixel.__fields__ - ['x', 'y', 'r', 'g', 'b'] - -You must manage the numbering yourself. - - -Creating NamedConstants ------------------------ - -A ``NamedConstant`` class is created much like an ``Enum``:: - - >>> from aenum import NamedConstant - >>> class Konstant(NamedConstant): - ... PI = 3.14159 - ... TAU = 2 * PI - - >>> Konstant.PI - - - >> print(Konstant.PI) - 3.14159 - - >>> Konstant.PI = 'apple' - Traceback (most recent call last): - ... - AttributeError: cannot rebind constant - - >>> del Konstant.PI - Traceback (most recent call last): - ... - AttributeError: cannot delete constant diff --git a/venv/Lib/site-packages/aenum/test.py b/venv/Lib/site-packages/aenum/test.py deleted file mode 100644 index 224293cb..00000000 --- a/venv/Lib/site-packages/aenum/test.py +++ /dev/null @@ -1,6832 +0,0 @@ -# -*- coding: utf-8 -*- - -from __future__ import division, print_function -import sys -import aenum -import doctest -import os -import shutil -import tempfile -import textwrap -import unittest -import uuid -import warnings -from aenum import EnumType, EnumMeta, Enum, IntEnum, StrEnum, LowerStrEnum, UpperStrEnum -from aenum import AutoNumberEnum, MultiValueEnum, OrderedEnum, UniqueEnum, AddValueEnum, Flag, IntFlag -from aenum import NamedTuple, TupleSize, NamedConstant, constant, NoAlias, AddValue, Unique -from aenum import STRICT, CONFORM, EJECT, KEEP -from aenum import _reduce_ex_by_name, unique, skip, extend_enum, auto, enum, MultiValue, member, nonmember, no_arg -from aenum import basestring, baseinteger, unicode, enum_property -from aenum import pyver, PY2, PY3, PY2_6, PY3_3, PY3_4, PY3_5, PY3_6, PY3_11 -from collections import OrderedDict -from datetime import timedelta -from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL -from operator import or_ as _or_, and_ as _and_, xor as _xor_, inv as _inv_ -from operator import abs as _abs_, add as _add_, floordiv as _floordiv_ -from operator import lshift as _lshift_, rshift as _rshift_, mod as _mod_ -from operator import mul as _mul_, neg as _neg_, pos as _pos_, pow as _pow_ -from operator import truediv as _truediv_, sub as _sub_ -if PY2: - from operator import div as _div_ -try: - import threading -except ImportError: - threading = None - -try: - any -except NameError: - from aenum import any - -MODULE = __name__ -SHORT_MODULE = MODULE.split('.')[-1] - -def load_tests(loader, tests, ignore): - tests.addTests(doctest.DocTestSuite(aenum)) - tests.addTests(doctest.DocFileSuite( - 'doc/aenum.rst', - package=aenum, - optionflags=doctest.ELLIPSIS|doctest.NORMALIZE_WHITESPACE, - )) - return tests - -class TestCase(unittest.TestCase): - - def __init__(self, *args, **kwds): - regex = getattr(self, 'assertRaisesRegex', None) - if regex is None: - self.assertRaisesRegex = getattr(self, 'assertRaisesRegexp') - super(TestCase, self).__init__(*args, **kwds) - - -# for pickle tests -try: - class Stooges(Enum): - LARRY = 1 - CURLY = 2 - MOE = 3 -except Exception: - Stooges = sys.exc_info()[1] - -try: - class IntStooges(int, Enum): - LARRY = 1 - CURLY = 2 - MOE = 3 -except Exception: - IntStooges = sys.exc_info()[1] - -try: - class FloatStooges(float, Enum): - LARRY = 1.39 - CURLY = 2.72 - MOE = 3.142596 -except Exception: - FloatStooges = sys.exc_info()[1] - -try: - class FlagStooges(Flag): - LARRY = 1 - CURLY = 2 - MOE = 3 -except Exception as exc: - FlagStooges = exc - -try: - LifeForm = NamedTuple('LifeForm', 'branch genus species', module=__name__) -except Exception: - LifeForm = sys.exc_info()[1] - -try: - class DeathForm(NamedTuple): - color = 0 - rigidity = 1 - odor = 2 -except Exception: - DeathForm = sys.exc_info()[1] - -# for pickle test and subclass tests -try: - class Name(StrEnum): - BDFL = 'Guido van Rossum' - FLUFL = 'Barry Warsaw' -except Exception: - Name = sys.exc_info()[1] - -try: - Question = Enum('Question', 'who what when where why', module=__name__) -except Exception: - Question = sys.exc_info()[1] - -try: - Answer = Enum('Answer', 'him this then there because') -except Exception: - Answer = sys.exc_info()[1] - -try: - class WhatsIt(NamedTuple): - def what(self): - return self[0] - class ThatsIt(WhatsIt): - blah = 0 - bleh = 1 -except Exception: - ThatsIt = sys.exc_info()[1] - -# for doctests -try: - class Fruit(Enum): - tomato = 1 - banana = 2 - cherry = 3 -except Exception: - pass - -def test_pickle_dump_load(assertion, source, target=None, protocol=(0, HIGHEST_PROTOCOL)): - start, stop = protocol - failures = [] - for protocol in range(start, stop+1): - try: - if target is None: - assertion(loads(dumps(source, protocol=protocol)), source) - else: - assertion(loads(dumps(source, protocol=protocol)), target) - except Exception: - exc, tb = sys.exc_info()[1:] - failures.append('%2d: %s' %(protocol, exc)) - if failures: - raise ValueError('Failed with protocols: %s' % ', '.join(failures)) - -def test_pickle_exception(assertion, exception, obj, - protocol=(0, HIGHEST_PROTOCOL)): - start, stop = protocol - failures = [] - for protocol in range(start, stop+1): - try: - assertion(exception, dumps, obj, protocol=protocol) - except Exception: - exc = sys.exc_info()[1] - failures.append('%d: %s %s' % (protocol, exc.__class__.__name__, exc)) - if failures: - raise ValueError('Failed with protocols: %s' % ', '.join(failures)) - -if PY3: - from aenum.test_v3 import TestEnumV3, TestOrderV3, TestNamedTupleV3, TestStackoverflowAnswersV3, TestIssuesV3, TestExtendEnumV3 - from aenum import test_v3 - test_v3.IntStooges = IntStooges - test_v3.test_pickle_exception = test_pickle_exception - test_v3.test_pickle_dump_load = test_pickle_dump_load - -# for subclassing tests - -class classproperty(object): - - def __init__(self, fget=None, fset=None, fdel=None, doc=None): - self.fget = fget - self.fset = fset - self.fdel = fdel - if doc is None and fget is not None: - doc = fget.__doc__ - self.__doc__ = doc - - def __get__(self, instance, ownerclass): - return self.fget(ownerclass) - - -# tests -class TestOrder(TestCase): - """ - Test _order_ extra/missing members. - """ - - def test_same_members(self): - class Color(Enum): - _order_ = 'red green blue' - red = 1 - green = 2 - blue = 3 - - def test_same_members_with_aliases(self): - class Color(Enum): - _order_ = 'red green blue' - red = 1 - green = 2 - blue = 3 - verde = green - - def test_order_has_extra_members(self): - with self.assertRaisesRegex(TypeError, r'member order does not match _order_'): - class Color(Enum): - _order_ = 'red green blue purple' - red = 1 - green = 2 - blue = 3 - - def test_order_has_extra_members_with_aliases(self): - with self.assertRaisesRegex(TypeError, r'member order does not match _order_'): - class Color(Enum): - _order_ = 'red green blue purple' - red = 1 - green = 2 - blue = 3 - verde = green - - def test_enum_has_extra_members(self): - with self.assertRaisesRegex(TypeError, r'member order does not match _order_'): - class Color(Enum): - _order_ = 'red green blue' - red = 1 - green = 2 - blue = 3 - purple = 4 - - def test_enum_has_extra_members_with_aliases(self): - with self.assertRaisesRegex(TypeError, r'member order does not match _order_'): - class Color(Enum): - _order_ = 'red green blue' - red = 1 - green = 2 - blue = 3 - purple = 4 - verde = green - - def test_same_members_flag(self): - class Color(Flag): - _order_ = 'red green blue' - red = 1 - green = 2 - blue = 4 - - def test_same_members_with_aliases_flag(self): - class Color(Flag): - _order_ = 'red green blue' - red = 1 - green = 2 - blue = 4 - verde = green - - def test_order_has_extra_members_flag(self): - with self.assertRaisesRegex(TypeError, r'member order does not match _order_'): - class Color(Flag): - _order_ = 'red green blue purple' - red = 1 - green = 2 - blue = 4 - - def test_order_has_extra_members_with_aliases_flag(self): - with self.assertRaisesRegex(TypeError, r'member order does not match _order_'): - class Color(Flag): - _order_ = 'red green blue purple' - red = 1 - green = 2 - blue = 4 - verde = green - - def test_enum_has_extra_members_flag(self): - with self.assertRaisesRegex(TypeError, r'member order does not match _order_'): - class Color(Flag): - _order_ = 'red green blue' - red = 1 - green = 2 - blue = 4 - purple = 8 - - def test_enum_has_extra_members_with_aliases_flag(self): - with self.assertRaisesRegex(TypeError, r'member order does not match _order_'): - class Color(Flag): - _order_ = 'red green blue' - red = 1 - green = 2 - blue = 4 - purple = 8 - verde = green - - -class TestAutoValue(TestCase): - - def test_bare(self): - # - class BareEnum(Enum): - _order_ = 'ONE TWO THREE' - ONE = auto() - TWO = auto() - THREE = auto() - self.assertEqual(BareEnum.THREE.value, 3) - # - class BareIntEnum(IntEnum): - _order_ = 'ONE TWO THREE' - ONE = auto() - TWO = auto() - THREE = auto() - self.assertEqual(BareIntEnum.THREE, 3) - # - class BareFlag(Flag): - _order_ = 'ONE TWO THREE' - ONE = auto() - TWO = auto() - THREE = auto() - self.assertEqual(BareFlag.THREE.value, 4) - # - class BareIntFlag(IntFlag): - _order_ = 'ONE TWO THREE' - ONE = auto() - TWO = auto() - THREE = auto() - self.assertEqual(BareIntFlag.THREE, 4) - - def test_init_only_final(self): - # - class InitEnumValue(Enum): - _init_ = 'value description' - _order_ = 'ONE TWO THREE' - ONE = 'the loneliest number' - TWO = 'the number with you' - THREE = 'a triangle' - self.assertEqual(InitEnumValue.THREE.value, 3) - self.assertEqual(InitEnumValue.THREE.description, 'a triangle') - # - class InitEnum(Enum): - _init_ = 'value description' - _order_ = 'ONE TWO THREE' - ONE = 'the loneliest number' - TWO = 'the number with you' - THREE = 'a triangle' - self.assertEqual(InitEnum.THREE.value, 3) - self.assertEqual(InitEnum.THREE.description, 'a triangle') - # - class InitIntEnum(IntEnum): - _init_ = 'value description' - _order_ = 'ONE TWO THREE' - ONE = 'the loneliest number' - TWO = 'the number with you' - THREE = 'a triangle' - self.assertEqual(InitIntEnum.THREE, 3) - self.assertEqual(InitIntEnum.THREE.description, 'a triangle') - # - class InitFlag(Flag): - _init_ = 'value description' - _order_ = 'ONE TWO THREE' - ONE = 'the loneliest number' - TWO = 'the number with you' - THREE = 'a triangle' - self.assertEqual(InitFlag.THREE.value, 4) - self.assertEqual(InitFlag.THREE.description, 'a triangle') - # - class InitIntFlag(IntFlag): - _init_ = 'value description' - _order_ = 'ONE TWO THREE' - ONE = 'the loneliest number' - TWO = 'the number with you' - THREE = 'a triangle' - self.assertEqual(InitIntFlag.THREE, 4) - self.assertEqual(InitIntFlag.THREE.description, 'a triangle') - - def test_init_only_inherit(self): - # - class InitInheritEnum(Enum): - _init_ = 'value description' - # - class InitEnum(InitInheritEnum): - _order_ = 'ONE TWO THREE' - ONE = 'the loneliest number' - TWO = 'the number with you' - THREE = 'a triangle' - self.assertEqual(InitEnum.THREE.value, 3) - self.assertEqual(InitEnum.THREE.description, 'a triangle') - # - # - class InitInheritValueEnum(Enum): - _init_ = 'value description' - # - class InitEnum(InitInheritValueEnum): - _order_ = 'ONE TWO THREE' - ONE = 'the loneliest number' - TWO = 'the number with you' - THREE = 'a triangle' - self.assertEqual(InitEnum.THREE.value, 3) - self.assertEqual(InitEnum.THREE.description, 'a triangle') - # - class InitIntEnum(int, InitInheritValueEnum): - _order_ = 'ONE TWO THREE' - ONE = 'the loneliest number' - TWO = 'the number with you' - THREE = 'a triangle' - self.assertEqual(InitIntEnum.THREE, 3) - self.assertEqual(InitIntEnum.THREE.description, 'a triangle') - # - class InitInheritValueFlag(Flag): - _init_ = 'value description' - # - class InitFlag(InitInheritValueFlag): - _order_ = 'ONE TWO THREE' - ONE = 'the loneliest number' - TWO = 'the number with you' - THREE = 'a triangle' - self.assertEqual(InitFlag.THREE.value, 4) - self.assertEqual(InitFlag.THREE.description, 'a triangle') - # - class InitIntFlag(int, InitInheritValueFlag): - _order_ = 'ONE TWO THREE' - ONE = 'the loneliest number' - TWO = 'the number with you' - THREE = 'a triangle' - self.assertEqual(InitIntFlag.THREE, 4) - self.assertEqual(InitIntFlag.THREE.description, 'a triangle') - - def test_new_only_final(self): - # - class NewFinalEnum(Enum): - _order_ = 'ONE TWO THREE' - def __new__(cls, value): - member = object.__new__(cls) - member._value_ = value - member.proof = 'NFE1' - return member - ONE = auto() - TWO = auto() - THREE = auto() - self.assertEqual(NewFinalEnum.THREE.value, 3) - self.assertEqual(NewFinalEnum.TWO.proof, 'NFE1') - # - class NewFinalIntEnum(IntEnum): - _order_ = 'ONE TWO THREE' - def __new__(cls, value): - member = int.__new__(cls, value) - member._value_ = value - member.proof = 'NFE2' - return member - ONE = auto() - TWO = auto() - THREE = auto() - self.assertEqual(NewFinalIntEnum.THREE, 3) - self.assertEqual(NewFinalIntEnum.TWO.proof, 'NFE2') - # - class NewFinalFlag(Flag): - _order_ = 'ONE TWO THREE' - def __new__(cls, value): - member = object.__new__(cls) - member._value_ = value - member.proof = 'NFE3' - return member - ONE = auto() - TWO = auto() - THREE = auto() - self.assertEqual(NewFinalFlag.THREE.value, 4) - self.assertEqual(NewFinalFlag.TWO.proof, 'NFE3') - # - class NewFinalIntFlag(IntFlag): - _order_ = 'ONE TWO THREE' - def __new__(cls, value): - member = int.__new__(cls, value) - member._value_ = value - member.proof = 'NFE4' - return member - ONE = auto() - TWO = auto() - THREE = auto() - self.assertEqual(NewFinalIntFlag.THREE, 4) - self.assertEqual(NewFinalIntFlag.TWO.proof, 'NFE4') - # - class NewFinalStrEnum(str, Enum): - # - _order_ = "AllReset Bright FG_Cyan BG_Black" - # - def __new__(cls, value, code, description): - str_value = '\x1b[%sm' % code - obj = str.__new__(cls, str_value) - obj._value_ = value - obj.code = code - obj.description = description - return obj - # - __str__ = str.__str__ - # - AllReset = '0', 'reset all (colors and brightness)' - Bright = '1', 'bright lights!' - FG_Cyan = '36', 'cyan' - BG_Black = '40', 'black' - self.assertEqual(NewFinalStrEnum.FG_Cyan.value, 3) - self.assertEqual(NewFinalStrEnum.BG_Black.value, 4) - self.assertEqual(NewFinalStrEnum.AllReset.code, '0') - self.assertEqual(NewFinalStrEnum.Bright.description, 'bright lights!') - # - class NewFinalStrFlag(str, Flag): - # - _order_ = "AllReset Bright FG_Cyan BG_Black" - # - def __new__(cls, value, code, description): - str_value = '\x1b[%sm' % code - obj = str.__new__(cls, str_value) - obj._value_ = value - obj.code = code - obj.description = description - return obj - # - __str__ = str.__str__ - # - AllReset = '0', 'reset all (colors and brightness)' - Bright = '1', 'bright lights!' - FG_Cyan = '36', 'cyan' - BG_Black = '40', 'black' - self.assertEqual(NewFinalStrFlag.FG_Cyan.value, 4) - self.assertEqual(NewFinalStrFlag.BG_Black.value, 8) - self.assertEqual(NewFinalStrFlag.AllReset.code, '0') - self.assertEqual(NewFinalStrFlag.Bright.description, 'bright lights!') - - def test_new_only_inherited(self): - # - class NewInheritEnum(Enum): - def __new__(cls, value): - if cls._member_type_ is int: - member = int.__new__(cls, value*2) - else: - member = object.__new__(cls) - member._value_ = value * 2 - member.proof = 'NIE' - return member - # - class NewFinalEnum(NewInheritEnum): - _order_ = 'ONE TWO THREE' - ONE = auto() - TWO = auto() - THREE = auto() - self.assertEqual(NewFinalEnum.THREE.value, 6) - self.assertEqual(NewFinalEnum.TWO.proof, 'NIE') - # - class NewFinalIntEnum(int, NewInheritEnum): - _order_ = 'ONE TWO THREE' - ONE = auto() - TWO = auto() - THREE = auto() - self.assertEqual(NewFinalIntEnum.THREE, 6) - self.assertEqual(NewFinalIntEnum.TWO.proof, 'NIE') - # - class NewInheritFlag(Flag): - def __new__(cls, value): - if cls._member_type_ is int: - member = int.__new__(cls, value*2) - else: - member = object.__new__(cls) - member._value_ = value * 2 - member.proof = 'NIE' - return member - # - class NewFinalFlag(NewInheritFlag): - _order_ = 'ONE TWO THREE' - ONE = auto() - TWO = auto() - THREE = auto() - self.assertEqual(NewFinalFlag.THREE.value, 8) - self.assertEqual(NewFinalFlag.TWO.proof, 'NIE') - # - class NewFinalIntFlag(int, NewInheritFlag): - _order_ = 'ONE TWO THREE' - ONE = auto() - TWO = auto() - THREE = auto() - self.assertEqual(NewFinalIntFlag.THREE, 8) - self.assertEqual(NewFinalIntFlag.TWO.proof, 'NIE') - - def test_init_new_only(self): - # - class InitNewEnum(Enum): - _init_ = "value description" - _order_ = 'ONE TWO THREE' - def __new__(cls, value, *args): - member = object.__new__(cls) - member._value_ = value - member.proof = 'INE1' - return member - ONE = 'the loneliest number' - TWO = 'the number with you' - THREE = 'a triangle' - self.assertEqual(InitNewEnum.THREE.value, 3) - self.assertEqual(InitNewEnum.THREE.description, 'a triangle') - self.assertEqual(InitNewEnum.TWO.proof, 'INE1') - # - class InitNewIntEnum(IntEnum): - _init_ = "value description" - _order_ = 'ONE TWO THREE' - def __new__(cls, value, *args): - member = int.__new__(cls, value) - member._value_ = value - member.proof = 'INE2' - return member - ONE = 'the loneliest number' - TWO = 'the number with you' - THREE = 'a triangle' - self.assertEqual(InitNewIntEnum.THREE, 3) - self.assertEqual(InitNewIntEnum.THREE.description, 'a triangle') - self.assertEqual(InitNewIntEnum.TWO.proof, 'INE2') - # - class InitNewFlag(Flag): - _init_ = "value description" - _order_ = 'ONE TWO THREE' - def __new__(cls, value, *args): - member = object.__new__(cls) - member._value_ = value - member.proof = 'INE3' - return member - ONE = 'the loneliest number' - TWO = 'the number with you' - THREE = 'a triangle' - self.assertEqual(InitNewFlag.THREE.value, 4) - self.assertEqual(InitNewFlag.THREE.description, 'a triangle') - self.assertEqual(InitNewFlag.TWO.proof, 'INE3') - # - class InitNewIntFlag(IntFlag): - _init_ = "value description" - _order_ = 'ONE TWO THREE' - def __new__(cls, value, *args): - member = int.__new__(cls, value) - member._value_ = value - member.proof = 'INE4' - return member - ONE = 'the loneliest number' - TWO = 'the number with you' - THREE = 'a triangle' - self.assertEqual(InitNewIntFlag.THREE, 4) - self.assertEqual(InitNewIntFlag.THREE.description, 'a triangle') - self.assertEqual(InitNewIntFlag.TWO.proof, 'INE4') - - def test_init_new_inherit(self): - # - class InitNew(Enum): - _init_ = "value description" - def __new__(cls, value, *args): - member = object.__new__(cls) - member._value_ = value - member.proof = 'IN' - return member - # - class InitNewEnum(InitNew): - _order_ = 'ONE TWO THREE' - ONE = 'the loneliest number' - TWO = 'the number with you' - THREE = 'a triangle' - self.assertEqual(InitNewEnum.THREE.value, 3) - self.assertEqual(InitNewEnum.THREE.description, 'a triangle') - self.assertEqual(InitNewEnum.TWO.proof, 'IN') - # - class InitNewInt(Enum): - _init_ = "value description" - def __new__(cls, value, *args): - member = int.__new__(cls, value) - member._value_ = value - member.proof = 'IN' - return member - # - class InitNewIntEnum(int, InitNewInt): - _order_ = 'ONE TWO THREE' - ONE = 'the loneliest number' - TWO = 'the number with you' - THREE = 'a triangle' - self.assertEqual(InitNewIntEnum.THREE, 3) - self.assertEqual(InitNewIntEnum.THREE.description, 'a triangle') - self.assertEqual(InitNewIntEnum.TWO.proof, 'IN') - # - class InitNewFlagBase(Flag): - _init_ = "value description" - def __new__(cls, value, *args): - member = object.__new__(cls) - member._value_ = value - member.proof = 'IN' - return member - # - class InitNewFlag(InitNewFlagBase): - _order_ = 'ONE TWO THREE' - ONE = 'the loneliest number' - TWO = 'the number with you' - THREE = 'a triangle' - self.assertEqual(InitNewFlag.THREE.value, 4) - self.assertEqual(InitNewFlag.THREE.description, 'a triangle') - self.assertEqual(InitNewFlag.TWO.proof, 'IN') - # - class InitNewIntFlagBase(int, Flag): - _init_ = "value description" - def __new__(cls, value, *args): - member = int.__new__(cls, value) - member._value_ = value - member.proof = 'IN' - return member - # - class InitNewIntFlag(InitNewIntFlagBase): - _order_ = 'ONE TWO THREE' - ONE = 'the loneliest number' - TWO = 'the number with you' - THREE = 'a triangle' - self.assertEqual(InitNewIntFlag.THREE, 4) - self.assertEqual(InitNewIntFlag.THREE.description, 'a triangle') - self.assertEqual(InitNewIntFlag.TWO.proof, 'IN') - - -class TestHelpers(TestCase): - # _is_descriptor, _is_sunder, _is_dunder - - def test_is_descriptor(self): - class foo: - pass - for attr in ('__get__','__set__','__delete__'): - obj = foo() - self.assertFalse(aenum._is_descriptor(obj)) - setattr(obj, attr, 1) - self.assertTrue(aenum._is_descriptor(obj)) - - def test_is_sunder(self): - for s in ('_a_', '_aa_'): - self.assertTrue(aenum._is_sunder(s)) - - for s in ('a', 'a_', '_a', '__a', 'a__', '__a__', '_a__', '__a_', '_', - '__', '___', '____', '_____',): - self.assertFalse(aenum._is_sunder(s)) - - def test_is_dunder(self): - for s in ('__a__', '__aa__'): - self.assertTrue(aenum._is_dunder(s)) - for s in ('a', 'a_', '_a', '__a', 'a__', '_a_', '_a__', '__a_', '_', - '__', '___', '____', '_____',): - self.assertFalse(aenum._is_dunder(s)) - - def test_auto(self): - def tester(first, op, final, second=None): - if second is None: - left = auto() - value = op(left) - left.value = first - self.assertEqual(value.value, final, - "%s %r -> %r != %r" % (op.__name__, first, value, final)) - else: - left = first - right = auto() - value = op(left, right) - right.value = second - self.assertEqual(value.value, final, - "forward: %r %s %r -> %r != %r" % (first, op.__name__, second, value.value, final)) - left = auto() - right = second - value = op(left, right) - left.value = first - self.assertEqual(value.value, final, - "reversed: %r %s %r -> %r != %r" % (second, op.__name__, first, value.value, final)) - for args in ( - (1, _abs_, abs(1)), - (-3, _abs_, abs(-3)), - (1, _add_, 1+2, 2), - (25, _floordiv_, 25 // 5, 5), - (49, _truediv_, 49 / 9, 9), - (6, _mod_, 6 % 9, 9), - (5, _lshift_, 5 << 2, 2), - (5, _rshift_, 5 >> 2, 2), - (3, _mul_, 3 * 6, 6), - (5, _neg_, -5), - (-4, _pos_, +(-4)), - (2, _pow_, 2**5, 5), - (7, _sub_, 7 - 10, 10), - (1, _or_, 1 | 2, 2), - (3, _xor_, 3 ^ 6, 6), - (3, _and_, 3 & 6, 6), - (7, _inv_, ~7), - ('a', _add_, 'a'+'b', 'b'), - ('a', _mul_, 'a' * 3, 3), - ): - tester(*args) - # operator.div is gone in 3 - if PY2: - tester(12, _div_, 12 // 5, 5) - # strings are a pain - left = auto() - right = 'eggs' - value = _mod_(left, right) - left.value = 'I see 17 %s!' - self.assertEqual(value.value, 'I see 17 %s!' % 'eggs') - - def test_constant(self): - errors = [] - def tester(first, op, final, second=None): - if second is None: - primary = constant(first) - secondary = constant(op(primary)) - if secondary.value != final: - errors.append( - "%s %r -> %r != %r" % (op.__name__, first, secondary.value, final), - ) - else: - left = constant(first) - right = second - value = op(left, right) - if value != final: - errors.append( - "forward: %r %s %r -> %r != %r" % (first, op.__name__, second, value, final), - ) - left = first - right = constant(second) - value = op(left, right) - if value != final: - errors.append( - "reversed: %r %s %r -> %r != %r" % (second, op.__name__, first, value, final), - ) - for args in ( - (1, _abs_, abs(1)), - (-3, _abs_, abs(-3)), - (1, _add_, 1+2, 2), - (25, _floordiv_, 25 // 5, 5), - (49, _truediv_, 49 / 9, 9), - (6, _mod_, 6 % 9, 9), - (5, _lshift_, 5 << 2, 2), - (5, _rshift_, 5 >> 2, 2), - (3, _mul_, 3 * 6, 6), - (5, _neg_, -5), - (-4, _pos_, +(-4)), - (2, _pow_, 2**5, 5), - (7, _sub_, 7 - 10, 10), - (1, _or_, 1 | 2, 2), - (3, _xor_, 3 ^ 6, 6), - (3, _and_, 3 & 6, 6), - (7, _inv_, ~7), - ('a', _add_, 'a'+'b', 'b'), - ('a', _mul_, 'a' * 3, 3), - ): - tester(*args) - # operator.div is gone in 3 - if PY2: - tester(12, _div_, 12 // 5, 5) - # strings are a pain - left = constant('I see 17 %s!') - right = 'eggs' - value = _mod_(left, right) - if value != 'I see 17 %s!' % 'eggs': - errors.append("'I see 17 eggs!' != %r" % value) - if errors: - print() - for error in errors: - print(error) - self.assertTrue(False) - - -class TestEnumType(TestCase): - - def test_immutability(self): - class Hah(object): - @classproperty - def all_values(cls): - return [m.value for m in cls] - class Huh(Hah, Enum): - one = 1 - two = 2 - self.assertRaisesRegex(AttributeError, 'cannot rebind property', setattr, Huh, 'value', 'boom') - self.assertRaisesRegex(AttributeError, 'cannot delete property', delattr, Huh, 'value') - self.assertRaisesRegex(AttributeError, 'cannot set attribute', setattr, Huh.one, 'value', 'boom') - self.assertRaisesRegex(AttributeError, 'cannot delete attribute', delattr, Huh.two, 'value') - self.assertEqual(Huh.one.value, 1) - self.assertEqual(Huh.two.value, 2) - self.assertEqual(Huh.all_values, [1, 2]) - setattr(Huh, 'all_values', 99) - self.assertEqual(Huh.all_values, 99) - - def test_enum_shadow_base(self): - class hohum(object): - def cyan(self): - "cyanize a color" - return self.value * 'cyan' - @property - def azure(self): - return 'azure ' + self.name - class Color(hohum, Enum): - red = 1 - green = 2 - blue = 3 - cyan = 4 - azure = 5 - self.assertEqual(len(Color), 5) - self.assertEqual(list(Color), [Color.red, Color.green, Color.blue, Color.cyan, Color.azure]) - self.assertRaisesRegex(AttributeError, 'no attribute .cyan.', lambda: Color.blue.cyan) - self.assertEqual(Color.red.azure, 'azure red') - - -class TestEnum(TestCase): - - def setUp(self): - class Season(Enum): - SPRING = 1 - SUMMER = 2 - AUTUMN = 3 - WINTER = 4 - self.Season = Season - - class Konstants(float, Enum): - E = 2.7182818 - PI = 3.1415926 - TAU = 2 * PI - self.Konstants = Konstants - - class Grades(IntEnum): - A = 5 - B = 4 - C = 3 - D = 2 - F = 0 - self.Grades = Grades - - class Directional(str, Enum): - EAST = 'east' - WEST = 'west' - NORTH = 'north' - SOUTH = 'south' - self.Directional = Directional - - from datetime import date - class Holiday(date, Enum): - NEW_YEAR = 2013, 1, 1 - IDES_OF_MARCH = 2013, 3, 15 - self.Holiday = Holiday - - def test_set_name(self): - class Descriptor(object): - name = None - def __get__(self, instance, owner_class=None): - if instance is None: - return self - else: - return instance.__dict__[self.name] - def __set__(self, instance, value): - instance.__dict__[self.name] = value - def __set_name__(self, owner, name): - self.name = name - # - class AnEnum(Enum): - ONE = 'one' - two = Descriptor() - # - self.assertEqual(list(AnEnum), [AnEnum.ONE]) - self.assertEqual(AnEnum.two.name, 'two') - AnEnum.ONE.two = 'three' - self.assertEqual(AnEnum.ONE.two, 'three') - self.assertEqual(AnEnum.ONE.__dict__['two'], 'three') - - def test_private_names(self): - class Private(Enum): - __corporal = 'Radar' - __major_ = 'Hoolihan' - self.assertEqual(len(Private), 0) - self.assertEqual(Private._Private__corporal, 'Radar') - self.assertFalse(isinstance(Private._Private__corporal, Enum)) - self.assertEqual(Private._Private__major_, 'Hoolihan') - self.assertFalse(isinstance(Private._Private__major_, Enum)) - - def test_new_with_keywords(self): - class Huh(IntEnum): - __order__ = 'PLAIN BOLD_ITALIC HIGHLIGHT' - def __new__(cls, docstring, open=None, close=None): - value = len(cls.__members__) - member = int.__new__(cls, value) - if open and close is None: - close = open - member.open = open - member.close = close - member.__doc__ = docstring - member._value_ = value - return member - PLAIN = 'normal' - BOLD_ITALIC = '***really super important***', '***' - HIGHLIGHT = 'please ==take notice==', '==', '==' - p = Huh.PLAIN - self.assertTrue(type(p) is Huh, type(p)) - self.assertEqual( - (p.value, p.__doc__, p.open, p.close), - (0, 'normal', None, None), - ) - bi = Huh.BOLD_ITALIC - self.assertEqual( - (bi.value, bi.__doc__, bi.open, bi.close), - (1, '***really super important***', '***', '***'), - ) - h = Huh.HIGHLIGHT - self.assertEqual( - (h.value, h.__doc__, h.open, h.close), - (2, 'please ==take notice==', '==', '=='), - ) - - def test_members_is_ordereddict_if_ordered(self): - class Ordered(Enum): - __order__ = 'first second third' - first = 'bippity' - second = 'boppity' - third = 'boo' - self.assertTrue(type(Ordered.__members__) is OrderedDict) - - def test_members_is_ordereddict_if_not_ordered(self): - class Unordered(Enum): - this = 'that' - these = 'those' - self.assertTrue(type(Unordered.__members__) is OrderedDict) - - def test_enum_in_enum_out(self): - Season = self.Season - self.assertTrue(Season(Season.WINTER) is Season.WINTER) - - def test_enum_value(self): - Season = self.Season - self.assertEqual(Season.SPRING.value, 1) - - def test_intenum_value(self): - self.assertEqual(IntStooges.CURLY.value, 2) - - def test_enum(self): - Season = self.Season - lst = list(Season) - self.assertEqual(len(lst), len(Season)) - self.assertEqual(len(Season), 4, Season) - self.assertEqual( - [Season.SPRING, Season.SUMMER, Season.AUTUMN, Season.WINTER], lst) - - for i, season in enumerate('SPRING SUMMER AUTUMN WINTER'.split()): - i += 1 - e = Season(i) - self.assertEqual(e, getattr(Season, season)) - self.assertEqual(e.value, i) - self.assertNotEqual(e, i) - self.assertEqual(e.name, season) - self.assertTrue(e in Season) - self.assertTrue(type(e) is Season) - self.assertTrue(isinstance(e, Season)) - self.assertEqual(str(e), 'Season.' + season) - self.assertEqual( - repr(e), - '' % (season, i), - ) - def test_enum_helper(self): - e1 = enum(1, 2, three=9) - e2 = enum(1, 2, three=9) - e3 = enum(1, 2, 9) - self.assertTrue(e1 is not e2) - self.assertEqual(e1, e2) - self.assertNotEqual(e1, e3) - self.assertNotEqual(e2, e3) - - def test_enum_in_enum(self): - # - class Level(Enum): - _order_ = 'DATA_CHECK DESIGN_CHECK ALERT' - # - def __new__(cls, *args, **kwds): - member = object.__new__(cls) - member._value_ = len(cls) + 1 # members are 1-based - return member - # - def __init__(self, prereq=None, dependent=None): - # create priority level lists - self.lower_priority_levels = list(self.__class__._member_map_.values()) - self.greater_priority_levels = [] - # update previous members' greater priority list - for member in self.lower_priority_levels: - member.greater_priority_levels.append(self) - # and save prereq and dependent - self.prerequisite = prereq and self.__class__[prereq.name] or None - self.dependent = dependent and self.__class__[dependent.name] or None - # - DATA_CHECK = enum() - DESIGN_CHECK = enum(DATA_CHECK) - ALERT = enum(None, DATA_CHECK) - # - self.assertEqual(Level.DATA_CHECK.value, 1) - self.assertEqual(Level.DATA_CHECK.prerequisite, None) - self.assertEqual(Level.DATA_CHECK.dependent, None) - self.assertEqual(Level.DESIGN_CHECK.prerequisite, Level.DATA_CHECK) - self.assertEqual(Level.DESIGN_CHECK.dependent, None) - self.assertEqual(Level.ALERT.prerequisite, None) - self.assertEqual(Level.ALERT.dependent, Level.DATA_CHECK) - - def test_value_name(self): - Season = self.Season - self.assertEqual(Season.SPRING.name, 'SPRING') - self.assertEqual(Season.SPRING.value, 1) - def set_name(obj, new_value): - obj.name = new_value - def set_value(obj, new_value): - obj.value = new_value - self.assertRaises(AttributeError, set_name, Season.SPRING, 'invierno', ) - self.assertRaises(AttributeError, set_value, Season.SPRING, 2) - - def test_attribute_deletion(self): - class Season(Enum): - SPRING = 1 - SUMMER = 2 - AUTUMN = 3 - WINTER = 4 - - def spam(cls): - pass - - self.assertTrue(hasattr(Season, 'spam')) - del Season.spam - self.assertFalse(hasattr(Season, 'spam')) - - self.assertRaises(AttributeError, delattr, Season, 'SPRING') - self.assertRaises(AttributeError, delattr, Season, 'DRY') - self.assertRaises(AttributeError, delattr, Season.SPRING, 'name') - - def test_bool_of_class(self): - class Empty(Enum): - pass - self.assertTrue(bool(Empty)) - - def test_bool_of_member(self): - class Count(Enum): - zero = 0 - one = 1 - two = 2 - for member in Count: - self.assertTrue(bool(member)) - - def test_invalid_names(self): - def create_bad_class_1(): - class Wrong(Enum): - mro = 9 - def create_bad_class_2(): - class Wrong(Enum): - _reserved_ = 3 - self.assertRaises(ValueError, create_bad_class_1) - self.assertRaises(ValueError, create_bad_class_2) - - def test_bool(self): - class Logic(Enum): - true = True - false = False - def __bool__(self): - return bool(self.value) - __nonzero__ = __bool__ - self.assertTrue(Logic.true) - self.assertFalse(Logic.false) - - def test_contains(self): - Season = self.Season - self.assertRaises(TypeError, lambda: 'AUTUMN' in Season) - self.assertTrue(Season.AUTUMN in Season) - self.assertRaises(TypeError, lambda: 3 not in Season) - val = Season(3) - self.assertTrue(val in Season) - # - class OtherEnum(Enum): - one = 1; two = 2 - self.assertTrue(OtherEnum.two not in Season) - # - class Wierd(Enum): - this = [1, 2, 3] - that = (1, 2, 3) - those = {1: 1, 2: 2, 3: 3} - self.assertTrue(Wierd.this in Wierd) - self.assertRaises(TypeError, lambda: [1, 2, 3] in Wierd) - self.assertRaises(TypeError, lambda: {1: 1, 2: 2, 3: 3} in Wierd) - - def test_member_contains(self): - self.assertRaises(TypeError, lambda: 'test' in self.Season.AUTUMN) - - if pyver >= PY2_6: # when `format` came into being - - def test_format_enum(self): - Season = self.Season - self.assertEqual('{0}'.format(Season.SPRING), - '{0}'.format(str(Season.SPRING))) - self.assertEqual( '{0:}'.format(Season.SPRING), - '{0:}'.format(str(Season.SPRING))) - self.assertEqual('{0:20}'.format(Season.SPRING), - '{0:20}'.format(str(Season.SPRING))) - self.assertEqual('{0:^20}'.format(Season.SPRING), - '{0:^20}'.format(str(Season.SPRING))) - self.assertEqual('{0:>20}'.format(Season.SPRING), - '{0:>20}'.format(str(Season.SPRING))) - self.assertEqual('{0:<20}'.format(Season.SPRING), - '{0:<20}'.format(str(Season.SPRING))) - - def test_custom_format(self): - class TestFloat(float, Enum): - one = 1.0 - two = 2.0 - def __format__(self, spec): - return 'TestFloat success!' - self.assertEqual(str(TestFloat.one), 'TestFloat.one') - self.assertEqual('{0}'.format(TestFloat.one), 'TestFloat success!') - - def test_format_with_custom_str(self): - class TestInt(int, Enum): - one = 1 - two = 2 - def __str__(self): - return self.name * 3 - self.assertEqual(str(TestInt.two), 'twotwotwo') - self.assertEqual('{0}'.format(TestInt.two), 'twotwotwo') - - def assertFormatIsValue(self, spec, member): - self.assertEqual(spec.format(member), spec.format(member.value)) - - def test_format_enum_date(self): - Holiday = self.Holiday - self.assertFormatIsValue('{0}', Holiday.IDES_OF_MARCH) - self.assertFormatIsValue('{0:}', Holiday.IDES_OF_MARCH) - self.assertFormatIsValue('{0:20}', Holiday.IDES_OF_MARCH) - self.assertFormatIsValue('{0:^20}', Holiday.IDES_OF_MARCH) - self.assertFormatIsValue('{0:>20}', Holiday.IDES_OF_MARCH) - self.assertFormatIsValue('{0:<20}', Holiday.IDES_OF_MARCH) - self.assertFormatIsValue('{0:%Y %m}', Holiday.IDES_OF_MARCH) - self.assertFormatIsValue('{0:%Y %m %M:00}', Holiday.IDES_OF_MARCH) - - def test_format_enum_float(self): - Konstants = self.Konstants - self.assertFormatIsValue('{0}', Konstants.TAU) - self.assertFormatIsValue('{0:}', Konstants.TAU) - self.assertFormatIsValue('{0:20}', Konstants.TAU) - self.assertFormatIsValue('{0:^20}', Konstants.TAU) - self.assertFormatIsValue('{0:>20}', Konstants.TAU) - self.assertFormatIsValue('{0:<20}', Konstants.TAU) - self.assertFormatIsValue('{0:n}', Konstants.TAU) - self.assertFormatIsValue('{0:5.2}', Konstants.TAU) - self.assertFormatIsValue('{0:f}', Konstants.TAU) - - def test_format_enum_int(self): - Grades = self.Grades - self.assertFormatIsValue('{0}', Grades.C) - self.assertFormatIsValue('{0:}', Grades.C) - self.assertFormatIsValue('{0:20}', Grades.C) - self.assertFormatIsValue('{0:^20}', Grades.C) - self.assertFormatIsValue('{0:>20}', Grades.C) - self.assertFormatIsValue('{0:<20}', Grades.C) - self.assertFormatIsValue('{0:+}', Grades.C) - self.assertFormatIsValue('{0:08X}', Grades.C) - self.assertFormatIsValue('{0:b}', Grades.C) - - def test_format_enum_str(self): - Directional = self.Directional - self.assertFormatIsValue('{0}', Directional.WEST) - self.assertFormatIsValue('{0:}', Directional.WEST) - self.assertFormatIsValue('{0:20}', Directional.WEST) - self.assertFormatIsValue('{0:^20}', Directional.WEST) - self.assertFormatIsValue('{0:>20}', Directional.WEST) - self.assertFormatIsValue('{0:<20}', Directional.WEST) - - def test_hash(self): - Season = self.Season - dates = {} - dates[Season.WINTER] = '1225' - dates[Season.SPRING] = '0315' - dates[Season.SUMMER] = '0704' - dates[Season.AUTUMN] = '1031' - self.assertEqual(dates[Season.AUTUMN], '1031') - - def test_enum_duplicates(self): - class Season(Enum): - __order__ = "SPRING SUMMER AUTUMN WINTER" - SPRING = 1 - SUMMER = 2 - AUTUMN = FALL = 3 - WINTER = 4 - ANOTHER_SPRING = 1 - lst = list(Season) - self.assertEqual( - lst, - [Season.SPRING, Season.SUMMER, - Season.AUTUMN, Season.WINTER, - ]) - self.assertTrue(Season.FALL is Season.AUTUMN) - self.assertEqual(Season.FALL.value, 3) - self.assertEqual(Season.AUTUMN.value, 3) - self.assertTrue(Season(3) is Season.AUTUMN) - self.assertTrue(Season(1) is Season.SPRING) - self.assertEqual(Season.FALL.name, 'AUTUMN') - self.assertEqual( - set([k for k,v in Season.__members__.items() if v.name != k]), - set(['FALL', 'ANOTHER_SPRING']), - ) - - def test_enum_with_value_name(self): - class Huh(Enum): - _order_ = 'name value' - name = 1 - value = 2 - self.assertEqual( - list(Huh), - [Huh.name, Huh.value], - ) - self.assertTrue(type(Huh.name) is Huh) - self.assertEqual(Huh.name.name, 'name') - self.assertEqual(Huh.name.value, 1) - - def test_intenum_from_scratch(self): - class phy(int, Enum): - pi = 3 - tau = 2 * pi - self.assertTrue(phy.pi < phy.tau) - - def test_intenum_inherited(self): - class IntEnum(int, Enum): - pass - class phy(IntEnum): - pi = 3 - tau = 2 * pi - self.assertTrue(phy.pi < phy.tau) - - def test_floatenum_from_scratch(self): - class phy(float, Enum): - pi = 3.1415926 - tau = 2 * pi - self.assertTrue(phy.pi < phy.tau) - - def test_floatenum_inherited(self): - class FloatEnum(float, Enum): - pass - class phy(FloatEnum): - pi = 3.1415926 - tau = 2 * pi - self.assertTrue(phy.pi < phy.tau) - - def test_strenum_from_scratch(self): - class phy(str, Enum): - pi = 'Pi' - tau = 'Tau' - self.assertTrue(phy.pi < phy.tau) - - def test_intenum(self): - class WeekDay(IntEnum): - SUNDAY = 1 - MONDAY = 2 - TUESDAY = 3 - WEDNESDAY = 4 - THURSDAY = 5 - FRIDAY = 6 - SATURDAY = 7 - - self.assertEqual(['a', 'b', 'c'][WeekDay.MONDAY], 'c') - self.assertEqual([i for i in range(WeekDay.TUESDAY)], [0, 1, 2]) - - lst = list(WeekDay) - self.assertEqual(len(lst), len(WeekDay)) - self.assertEqual(len(WeekDay), 7) - target = 'SUNDAY MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY' - target = target.split() - for i, weekday in enumerate(target): - i += 1 - e = WeekDay(i) - self.assertEqual(e, i) - self.assertEqual(int(e), i) - self.assertEqual(e.name, weekday) - self.assertTrue(e in WeekDay) - self.assertEqual(lst.index(e)+1, i) - self.assertTrue(0 < e < 8) - self.assertTrue(type(e) is WeekDay) - self.assertTrue(isinstance(e, int)) - self.assertTrue(isinstance(e, Enum)) - - def test_intenum_duplicates(self): - class WeekDay(IntEnum): - __order__ = 'SUNDAY MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY' - SUNDAY = 1 - MONDAY = 2 - TUESDAY = TEUSDAY = 3 - WEDNESDAY = 4 - THURSDAY = 5 - FRIDAY = 6 - SATURDAY = 7 - self.assertTrue(WeekDay.TEUSDAY is WeekDay.TUESDAY) - self.assertEqual(WeekDay(3).name, 'TUESDAY') - self.assertEqual([k for k,v in WeekDay.__members__.items() - if v.name != k], ['TEUSDAY', ]) - - def test_floatenum_fromhex(self): - h = float.hex(FloatStooges.MOE.value) - self.assertIs(FloatStooges.fromhex(h), FloatStooges.MOE) - h = float.hex(FloatStooges.MOE.value + 0.01) - with self.assertRaises(ValueError): - FloatStooges.fromhex(h) - - def test_pickle_enum(self): - if isinstance(Stooges, Exception): - raise Stooges - test_pickle_dump_load(self.assertTrue, Stooges.CURLY) - test_pickle_dump_load(self.assertTrue, Stooges) - - def test_pickle_int(self): - if isinstance(IntStooges, Exception): - raise IntStooges - test_pickle_dump_load(self.assertTrue, IntStooges.CURLY) - test_pickle_dump_load(self.assertTrue, IntStooges) - - def test_pickle_float(self): - if isinstance(FloatStooges, Exception): - raise FloatStooges - test_pickle_dump_load(self.assertTrue, FloatStooges.CURLY) - test_pickle_dump_load(self.assertTrue, FloatStooges) - - def test_pickle_enum_function(self): - if isinstance(Answer, Exception): - raise Answer - test_pickle_dump_load(self.assertTrue, Answer.him) - test_pickle_dump_load(self.assertTrue, Answer) - - def test_pickle_enum_function_with_module(self): - if isinstance(Question, Exception): - raise Question - test_pickle_dump_load(self.assertTrue, Question.who) - test_pickle_dump_load(self.assertTrue, Question) - - def test_pickle_by_name(self): - class ReplaceGlobalInt(IntEnum): - ONE = 1 - TWO = 2 - ReplaceGlobalInt.__reduce_ex__ = _reduce_ex_by_name - for proto in range(HIGHEST_PROTOCOL): - self.assertEqual(ReplaceGlobalInt.TWO.__reduce_ex__(proto), 'TWO') - - def test_exploding_pickle(self): - BadPickle = Enum('BadPickle', 'dill sweet bread-n-butter') - aenum._make_class_unpicklable(BadPickle) - globals()['BadPickle'] = BadPickle - test_pickle_exception(self.assertRaises, TypeError, BadPickle.dill) - test_pickle_exception(self.assertRaises, PicklingError, BadPickle) - - def test_string_enum(self): - class SkillLevel(str, Enum): - master = 'what is the sound of one hand clapping?' - journeyman = 'why did the chicken cross the road?' - apprentice = 'knock, knock!' - self.assertEqual(SkillLevel.apprentice, 'knock, knock!') - - def test_getattr_getitem(self): - class Period(Enum): - morning = 1 - noon = 2 - evening = 3 - night = 4 - self.assertTrue(Period(2) is Period.noon) - self.assertTrue(getattr(Period, 'night') is Period.night) - self.assertTrue(Period['morning'] is Period.morning) - - def test_getattr_dunder(self): - Season = self.Season - self.assertTrue(getattr(Season, '__hash__')) - - def test_iteration_order(self): - class Season(Enum): - __order__ = 'SUMMER WINTER AUTUMN SPRING' - SUMMER = 2 - WINTER = 4 - AUTUMN = 3 - SPRING = 1 - self.assertEqual( - list(Season), - [Season.SUMMER, Season.WINTER, Season.AUTUMN, Season.SPRING], - ) - - def test_iteration_order_reversed(self): - self.assertEqual( - list(reversed(self.Season)), - [self.Season.WINTER, self.Season.AUTUMN, self.Season.SUMMER, - self.Season.SPRING] - ) - - def test_iteration_order_with_unorderable_values(self): - class Complex(Enum): - a = complex(7, 9) - b = complex(3.14, 2) - c = complex(1, -1) - d = complex(-77, 32) - self.assertEqual( - list(Complex), - [Complex.a, Complex.b, Complex.c, Complex.d], - ) - - def test_programatic_function_string(self): - SummerMonth = Enum('SummerMonth', 'june july august') - lst = list(SummerMonth) - self.assertEqual(len(lst), len(SummerMonth)) - self.assertEqual(len(SummerMonth), 3, SummerMonth) - self.assertEqual( - [SummerMonth.june, SummerMonth.july, SummerMonth.august], - lst, - ) - for i, month in enumerate('june july august'.split()): - i += 1 - e = SummerMonth(i) - self.assertEqual(int(e.value), i) - self.assertNotEqual(e, i) - self.assertEqual(e.name, month) - self.assertTrue(e in SummerMonth) - self.assertTrue(type(e) is SummerMonth) - - def test_programatic_function_string_with_start(self): - SummerMonth = Enum('SummerMonth', 'june july august', start=10) - lst = list(SummerMonth) - self.assertEqual(len(lst), len(SummerMonth)) - self.assertEqual(len(SummerMonth), 3, SummerMonth) - self.assertEqual( - [SummerMonth.june, SummerMonth.july, SummerMonth.august], - lst, - ) - for i, month in enumerate('june july august'.split(), 10): - e = SummerMonth(i) - self.assertEqual(int(e.value), i) - self.assertNotEqual(e, i) - self.assertEqual(e.name, month) - self.assertTrue(e in SummerMonth) - self.assertTrue(type(e) is SummerMonth) - - def test_programatic_function_string_list(self): - SummerMonth = Enum('SummerMonth', ['june', 'july', 'august']) - lst = list(SummerMonth) - self.assertEqual(len(lst), len(SummerMonth)) - self.assertEqual(len(SummerMonth), 3, SummerMonth) - self.assertEqual( - [SummerMonth.june, SummerMonth.july, SummerMonth.august], - lst, - ) - for i, month in enumerate('june july august'.split()): - i += 1 - e = SummerMonth(i) - self.assertEqual(int(e.value), i) - self.assertNotEqual(e, i) - self.assertEqual(e.name, month) - self.assertTrue(e in SummerMonth) - self.assertTrue(type(e) is SummerMonth) - - def test_programatic_function_string_list_with_start(self): - SummerMonth = Enum('SummerMonth', ['june', 'july', 'august'], start=20) - lst = list(SummerMonth) - self.assertEqual(len(lst), len(SummerMonth)) - self.assertEqual(len(SummerMonth), 3, SummerMonth) - self.assertEqual( - [SummerMonth.june, SummerMonth.july, SummerMonth.august], - lst, - ) - for i, month in enumerate('june july august'.split(), 20): - e = SummerMonth(i) - self.assertEqual(int(e.value), i) - self.assertNotEqual(e, i) - self.assertEqual(e.name, month) - self.assertTrue(e in SummerMonth) - self.assertTrue(type(e) is SummerMonth) - - def test_programatic_function_iterable(self): - SummerMonth = Enum( - 'SummerMonth', - (('june', 1), ('july', 2), ('august', 3)) - ) - lst = list(SummerMonth) - self.assertEqual(len(lst), len(SummerMonth)) - self.assertEqual(len(SummerMonth), 3, SummerMonth) - self.assertEqual( - [SummerMonth.june, SummerMonth.july, SummerMonth.august], - lst, - ) - for i, month in enumerate('june july august'.split()): - i += 1 - e = SummerMonth(i) - self.assertEqual(int(e.value), i) - self.assertNotEqual(e, i) - self.assertEqual(e.name, month) - self.assertTrue(e in SummerMonth) - self.assertTrue(type(e) is SummerMonth) - - def test_programatic_function_from_dict(self): - SummerMonth = Enum( - 'SummerMonth', - dict((('june', 1), ('july', 2), ('august', 3))) - ) - lst = list(SummerMonth) - self.assertEqual(len(lst), len(SummerMonth)) - self.assertEqual(len(SummerMonth), 3, SummerMonth) - if PY2: - self.assertEqual( - [SummerMonth.june, SummerMonth.july, SummerMonth.august], - lst, - ) - for i, month in enumerate('june july august'.split()): - i += 1 - e = SummerMonth(i) - self.assertEqual(int(e.value), i) - self.assertNotEqual(e, i) - self.assertEqual(e.name, month) - self.assertTrue(e in SummerMonth) - self.assertTrue(type(e) is SummerMonth) - - def test_programatic_function_type(self): - SummerMonth = Enum('SummerMonth', 'june july august', type=int) - lst = list(SummerMonth) - self.assertEqual(len(lst), len(SummerMonth)) - self.assertEqual(len(SummerMonth), 3, SummerMonth) - self.assertEqual( - [SummerMonth.june, SummerMonth.july, SummerMonth.august], - lst, - ) - for i, month in enumerate('june july august'.split()): - i += 1 - e = SummerMonth(i) - self.assertEqual(e, i) - self.assertEqual(e.name, month) - self.assertTrue(e in SummerMonth) - self.assertTrue(type(e) is SummerMonth) - - def test_programatic_function_type_with_start(self): - SummerMonth = Enum('SummerMonth', 'june july august', type=int, start=30) - lst = list(SummerMonth) - self.assertEqual(len(lst), len(SummerMonth)) - self.assertEqual(len(SummerMonth), 3, SummerMonth) - self.assertEqual( - [SummerMonth.june, SummerMonth.july, SummerMonth.august], - lst, - ) - for i, month in enumerate('june july august'.split(), 30): - e = SummerMonth(i) - self.assertEqual(e, i) - self.assertEqual(e.name, month) - self.assertTrue(e in SummerMonth) - self.assertTrue(type(e) is SummerMonth) - - def test_programatic_function_type_from_subclass(self): - SummerMonth = IntEnum('SummerMonth', 'june july august') - lst = list(SummerMonth) - self.assertEqual(len(lst), len(SummerMonth)) - self.assertEqual(len(SummerMonth), 3, SummerMonth) - self.assertEqual( - [SummerMonth.june, SummerMonth.july, SummerMonth.august], - lst, - ) - for i, month in enumerate('june july august'.split()): - i += 1 - e = SummerMonth(i) - self.assertEqual(e, i) - self.assertEqual(e.name, month) - self.assertTrue(e in SummerMonth) - self.assertTrue(type(e) is SummerMonth) - - def test_programatic_function_type_from_subclass_with_start(self): - SummerMonth = IntEnum('SummerMonth', 'june july august', start=40) - lst = list(SummerMonth) - self.assertEqual(len(lst), len(SummerMonth)) - self.assertEqual(len(SummerMonth), 3, SummerMonth) - self.assertEqual( - [SummerMonth.june, SummerMonth.july, SummerMonth.august], - lst, - ) - for i, month in enumerate('june july august'.split(), 40): - e = SummerMonth(i) - self.assertEqual(e, i) - self.assertEqual(e.name, month) - self.assertTrue(e in SummerMonth) - self.assertTrue(type(e) is SummerMonth) - - def test_programatic_function_unicode(self): - SummerMonth = Enum('SummerMonth', unicode('june july august')) - lst = list(SummerMonth) - self.assertEqual(len(lst), len(SummerMonth)) - self.assertEqual(len(SummerMonth), 3, SummerMonth) - self.assertEqual( - [SummerMonth.june, SummerMonth.july, SummerMonth.august], - lst, - ) - for i, month in enumerate(unicode('june july august').split()): - i += 1 - e = SummerMonth(i) - self.assertEqual(int(e.value), i) - self.assertNotEqual(e, i) - self.assertEqual(e.name, month) - self.assertTrue(e in SummerMonth) - self.assertTrue(type(e) is SummerMonth) - - def test_programatic_function_unicode_list(self): - SummerMonth = Enum('SummerMonth', [unicode('june'), unicode('july'), unicode('august')]) - lst = list(SummerMonth) - self.assertEqual(len(lst), len(SummerMonth)) - self.assertEqual(len(SummerMonth), 3, SummerMonth) - self.assertEqual( - [SummerMonth.june, SummerMonth.july, SummerMonth.august], - lst, - ) - for i, month in enumerate(unicode('june july august').split()): - i += 1 - e = SummerMonth(i) - self.assertEqual(int(e.value), i) - self.assertNotEqual(e, i) - self.assertEqual(e.name, month) - self.assertTrue(e in SummerMonth) - self.assertTrue(type(e) is SummerMonth) - - def test_programatic_function_unicode_iterable(self): - SummerMonth = Enum( - 'SummerMonth', - ((unicode('june'), 1), (unicode('july'), 2), (unicode('august'), 3)) - ) - lst = list(SummerMonth) - self.assertEqual(len(lst), len(SummerMonth)) - self.assertEqual(len(SummerMonth), 3, SummerMonth) - self.assertEqual( - [SummerMonth.june, SummerMonth.july, SummerMonth.august], - lst, - ) - for i, month in enumerate(unicode('june july august').split()): - i += 1 - e = SummerMonth(i) - self.assertEqual(int(e.value), i) - self.assertNotEqual(e, i) - self.assertEqual(e.name, month) - self.assertTrue(e in SummerMonth) - self.assertTrue(type(e) is SummerMonth) - - def test_programatic_function_from_unicode_dict(self): - SummerMonth = Enum( - 'SummerMonth', - dict(((unicode('june'), 1), (unicode('july'), 2), (unicode('august'), 3))) - ) - lst = list(SummerMonth) - self.assertEqual(len(lst), len(SummerMonth)) - self.assertEqual(len(SummerMonth), 3, SummerMonth) - if PY2: - self.assertEqual( - [SummerMonth.june, SummerMonth.july, SummerMonth.august], - lst, - ) - for i, month in enumerate(unicode('june july august').split()): - i += 1 - e = SummerMonth(i) - self.assertEqual(int(e.value), i) - self.assertNotEqual(e, i) - self.assertEqual(e.name, month) - self.assertTrue(e in SummerMonth) - self.assertTrue(type(e) is SummerMonth) - - def test_programatic_function_unicode_type(self): - SummerMonth = Enum('SummerMonth', unicode('june july august'), type=int) - lst = list(SummerMonth) - self.assertEqual(len(lst), len(SummerMonth)) - self.assertEqual(len(SummerMonth), 3, SummerMonth) - self.assertEqual( - [SummerMonth.june, SummerMonth.july, SummerMonth.august], - lst, - ) - for i, month in enumerate(unicode('june july august').split()): - i += 1 - e = SummerMonth(i) - self.assertEqual(e, i) - self.assertEqual(e.name, month) - self.assertTrue(e in SummerMonth) - self.assertTrue(type(e) is SummerMonth) - - def test_programatic_function_unicode_type_from_subclass(self): - SummerMonth = IntEnum('SummerMonth', unicode('june july august')) - lst = list(SummerMonth) - self.assertEqual(len(lst), len(SummerMonth)) - self.assertEqual(len(SummerMonth), 3, SummerMonth) - self.assertEqual( - [SummerMonth.june, SummerMonth.july, SummerMonth.august], - lst, - ) - for i, month in enumerate(unicode('june july august').split()): - i += 1 - e = SummerMonth(i) - self.assertEqual(e, i) - self.assertEqual(e.name, month) - self.assertTrue(e in SummerMonth) - self.assertTrue(type(e) is SummerMonth) - - def test_programmatic_function_unicode_class(self): - if PY2: - class_names = unicode('SummerMonth'), 'S\xfcmm\xe9rM\xf6nth'.decode('latin1') - else: - class_names = 'SummerMonth', 'S\xfcmm\xe9rM\xf6nth' - for i, class_name in enumerate(class_names): - if PY2 and i == 1: - self.assertRaises(TypeError, Enum, class_name, unicode('june july august')) - else: - SummerMonth = Enum(class_name, unicode('june july august')) - lst = list(SummerMonth) - self.assertEqual(len(lst), len(SummerMonth)) - self.assertEqual(len(SummerMonth), 3, SummerMonth) - self.assertEqual( - [SummerMonth.june, SummerMonth.july, SummerMonth.august], - lst, - ) - for i, month in enumerate(unicode('june july august').split()): - i += 1 - e = SummerMonth(i) - self.assertEqual(e.value, i) - self.assertEqual(e.name, month) - self.assertTrue(e in SummerMonth) - self.assertTrue(type(e) is SummerMonth) - - def test_subclassing(self): - if isinstance(Name, Exception): - raise Name - self.assertEqual(Name.BDFL, 'Guido van Rossum') - self.assertTrue(Name.BDFL, Name('Guido van Rossum')) - self.assertTrue(Name.BDFL is getattr(Name, 'BDFL')) - test_pickle_dump_load(self.assertTrue, Name.BDFL) - - def test_extending(self): - def bad_extension(): - class Color(Enum): - red = 1 - green = 2 - blue = 3 - class MoreColor(Color): - cyan = 4 - magenta = 5 - yellow = 6 - self.assertRaises(TypeError, bad_extension) - - def test_exclude_methods(self): - class whatever(Enum): - this = 'that' - these = 'those' - def really(self): - return 'no, not %s' % self.value - self.assertFalse(type(whatever.really) is whatever) - self.assertEqual(whatever.this.really(), 'no, not that') - - def test_wrong_inheritance_order(self): - def wrong_inherit(): - class Wrong(Enum, str): - NotHere = 'error before this point' - self.assertRaises(TypeError, wrong_inherit) - - def test_intenum_transitivity(self): - class number(IntEnum): - one = 1 - two = 2 - three = 3 - class numero(IntEnum): - uno = 1 - dos = 2 - tres = 3 - self.assertEqual(number.one, numero.uno) - self.assertEqual(number.two, numero.dos) - self.assertEqual(number.three, numero.tres) - - def test_introspection(self): - class Number(IntEnum): - one = 100 - two = 200 - self.assertTrue(Number.one._member_type_ is int) - self.assertTrue(Number._member_type_ is int) - class String(str, Enum): - yarn = 'soft' - rope = 'rough' - wire = 'hard' - self.assertTrue(String.yarn._member_type_ is str) - self.assertTrue(String._member_type_ is str) - class Plain(Enum): - vanilla = 'white' - one = 1 - self.assertTrue(Plain.vanilla._member_type_ is object) - self.assertTrue(Plain._member_type_ is object) - - def test_wrong_enum_in_call(self): - class Monochrome(Enum): - black = 0 - white = 1 - class Gender(Enum): - male = 0 - female = 1 - self.assertRaises(ValueError, Monochrome, Gender.male) - - def test_wrong_enum_in_mixed_call(self): - class Monochrome(IntEnum): - black = 0 - white = 1 - class Gender(Enum): - male = 0 - female = 1 - self.assertRaises(ValueError, Monochrome, Gender.male) - - def test_mixed_enum_in_call_1(self): - class Monochrome(IntEnum): - black = 0 - white = 1 - class Gender(IntEnum): - male = 0 - female = 1 - self.assertTrue(Monochrome(Gender.female) is Monochrome.white) - - def test_mixed_enum_in_call_2(self): - class Monochrome(Enum): - black = 0 - white = 1 - class Gender(IntEnum): - male = 0 - female = 1 - self.assertTrue(Monochrome(Gender.male) is Monochrome.black) - - def test_flufl_enum(self): - class Fluflnum(Enum): - def __int__(self): - return int(self.value) - class MailManOptions(Fluflnum): - option1 = 1 - option2 = 2 - option3 = 3 - self.assertEqual(int(MailManOptions.option1), 1) - - def test_no_such_enum_member(self): - class Color(Enum): - red = 1 - green = 2 - blue = 3 - self.assertRaises(ValueError, Color, 4) - self.assertRaises(KeyError, Color.__getitem__, 'chartreuse') - - def test_new_repr(self): - class Color(Enum): - red = 1 - green = 2 - blue = 3 - def __repr__(self): - return "don't you just love shades of %s?" % self.name - self.assertEqual( - repr(Color.blue), - "don't you just love shades of blue?", - ) - - def test_inherited_repr(self): - class MyEnum(Enum): - def __repr__(self): - return "My name is %s." % self.name - class MyIntEnum(int, MyEnum): - this = 1 - that = 2 - theother = 3 - self.assertEqual(repr(MyIntEnum.that), "My name is that.") - - def test_multiple_mixin_mro(self): - class auto_enum(EnumMeta): - def __new__(metacls, cls, bases, classdict): - original_dict = classdict - temp_dict = metacls.__prepare__(cls, bases, {}) - if hasattr(original_dict, '_member_names'): - for k in original_dict._member_names: - temp_dict[k] = original_dict[k] - sunders = [k for k in original_dict.keys() if aenum._is_sunder(k)] - else: - sunders = [] - for k, v in original_dict.items(): - if aenum._is_sunder(k): - sunders.append(k) - temp_dict[k] = v - classdict = metacls.__prepare__(cls, bases, {}) - i = 0 - for k in sunders: - classdict[k] = original_dict[k] - for k in temp_dict._member_names: - v = original_dict[k] - if v == (): - v = i - else: - i = v - i += 1 - classdict[k] = v - for k, v in original_dict.items(): - if k not in temp_dict._member_names and k not in sunders: - classdict[k] = v - return super(auto_enum, metacls).__new__( - metacls, cls, bases, classdict) - - AutoNumberedEnum = auto_enum('AutoNumberedEnum', (Enum,), {}) - - AutoIntEnum = auto_enum('AutoIntEnum', (IntEnum,), {}) - - # class TestAutoNumber(AutoNumberedEnum): - # a = () - # b = 3 - # c = () - # self.assertEqual(TestAutoNumber.b.value, 3) - # - # if pyver >= 3.0: - # self.assertEqual( - # [TestAutoNumber.a.value, TestAutoNumber.b.value, TestAutoNumber.c.value], - # [0, 3, 4], - # ) - # - # class TestAutoInt(AutoIntEnum): - # a = () - # b = 3 - # c = () - # self.assertEqual(TestAutoInt.b, 3) - # - # if pyver >= 3.0: - # self.assertEqual( - # [TestAutoInt.a.value, TestAutoInt.b.value, TestAutoInt.c.value], - # [0, 3, 4], - # ) - - def test_meta_reconfigure(self): - - def identity(*args): - if len(args) == 1: - return args[0] - return args - - JSONEnum = None - - class JSONEnumMeta(EnumMeta): - - @classmethod - def __prepare__(metacls, cls, bases, init=None, start=None, settings=()): - return {} - - def __init__(cls, *args , **kwds): - super(JSONEnumMeta, cls).__init__(*args) - - def __new__(metacls, cls, bases, clsdict, init=None, start=None, settings=()): - import json - members = [] - if JSONEnum is not None: - if '_file' not in clsdict: - raise TypeError('_file is required') - if '_name' not in clsdict: - raise TypeError('_name is required') - if '_value' not in clsdict: - raise TypeError('_value is required') - name_spec = clsdict.pop('_name') - if not isinstance(name_spec, (tuple, list)): - name_spec = (name_spec, ) - value_spec = clsdict.pop('_value') - file = clsdict.pop('_file') - with open(file) as f: - json_data = json.load(f) - for data in json_data: - values = [] - name = data[name_spec[0]] - for piece in name_spec[1:]: - name = name[piece] - for order, (value_path, func) in sorted(value_spec.items()): - if not isinstance(value_path, (list, tuple)): - value_path = (value_path, ) - value = data[value_path[0]] - for piece in value_path[1:]: - value = value[piece] - if func is not None: - value = func(value) - values.append(value) - values = tuple(values) - members.append( - (name, identity(*values)) - ) - # get the real EnumDict - enum_dict = super(JSONEnumMeta, metacls).__prepare__(cls, bases, init, start, settings) - # transfer the original dict content, _items first - items = list(clsdict.items()) - items.sort(key=lambda p: (0 if p[0][0] == '_' else 1, p)) - for name, value in items: - enum_dict[name] = value - # add the members - for name, value in members: - enum_dict[name] = value - return super(JSONEnumMeta, metacls).__new__(metacls, cls, bases, enum_dict, init, start, settings) - - # for use with both Python 2/3 - JSONEnum = JSONEnumMeta('JsonEnum', (Enum, ), {}) - - test_file = os.path.join(tempdir, 'test_json.json') - with open(test_file, 'w') as f: - f.write( - '[{"name":"Afghanistan","alpha-2":"AF","country-code":"004","notes":{"description":"pretty"}},' - '{"name":"Åland Islands","alpha-2":"AX","country-code":"248","notes":{"description":"serene"}},' - '{"name":"Albania","alpha-2":"AL","country-code":"008","notes":{"description":"exciting"}},' - '{"name":"Algeria","alpha-2":"DZ","country-code":"012","notes":{"description":"scarce"}}]') - - class Country(JSONEnum): - _init_ = 'abbr code country_name description' - _file = test_file - _name = 'alpha-2' - _value = { - 1: ('alpha-2', None), - 2: ('country-code', lambda c: int(c)), - 3: ('name', None), - 4: (('notes','description'), lambda s: s.title()), - } - - self.assertEqual([Country.AF, Country.AX, Country.AL, Country.DZ], list(Country)) - self.assertEqual(Country.AF.abbr, 'AF') - self.assertEqual(Country.AX.code, 248) - self.assertEqual(Country.AL.country_name, 'Albania') - self.assertEqual(Country.DZ.description, 'Scarce') - - - def test_subclasses_with_getnewargs(self): - class NamedInt(int): - __qualname__ = 'NamedInt' # needed for pickle protocol 4 - def __new__(cls, *args): - _args = args - if len(args) < 1: - raise TypeError("name and value must be specified") - name, args = args[0], args[1:] - self = int.__new__(cls, *args) - self._intname = name - self._args = _args - return self - def __getnewargs__(self): - return self._args - @property - def __name__(self): - return self._intname - def __repr__(self): - # repr() is updated to include the name and type info - return "%s(%r, %s)" % (type(self).__name__, - self.__name__, - int.__repr__(self)) - def __str__(self): - # str() is unchanged, even if it relies on the repr() fallback - base = int - base_str = base.__str__ - if base_str.__objclass__ is object: - return base.__repr__(self) - return base_str(self) - # for simplicity, we only define one operator that - # propagates expressions - def __add__(self, other): - temp = int(self) + int( other) - if isinstance(self, NamedInt) and isinstance(other, NamedInt): - return NamedInt( - '(%s + %s)' % (self.__name__, other.__name__), - temp ) - else: - return temp - - class NEI(NamedInt, Enum): - __qualname__ = 'NEI' # needed for pickle protocol 4 - x = ('the-x', 1) - y = ('the-y', 2) - - self.assertTrue(NEI.__new__ is Enum.__new__) - self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") - globals()['NamedInt'] = NamedInt - globals()['NEI'] = NEI - NI5 = NamedInt('test', 5) - self.assertEqual(NI5, 5) - test_pickle_dump_load(self.assertTrue, NI5, 5) - self.assertEqual(NEI.y.value, 2) - test_pickle_dump_load(self.assertTrue, NEI.y) - - def test_subclasses_with_reduce(self): - class NamedInt(int): - __qualname__ = 'NamedInt' # needed for pickle protocol 4 - def __new__(cls, *args): - _args = args - if len(args) < 1: - raise TypeError("name and value must be specified") - name, args = args[0], args[1:] - self = int.__new__(cls, *args) - self._intname = name - self._args = _args - return self - def __reduce__(self): - return self.__class__, self._args - @property - def __name__(self): - return self._intname - def __repr__(self): - # repr() is updated to include the name and type info - return "%s(%r, %s)" % (type(self).__name__, - self.__name__, - int.__repr__(self)) - def __str__(self): - # str() is unchanged, even if it relies on the repr() fallback - base = int - base_str = base.__str__ - if base_str.__objclass__ is object: - return base.__repr__(self) - return base_str(self) - # for simplicity, we only define one operator that - # propagates expressions - def __add__(self, other): - temp = int(self) + int( other) - if isinstance(self, NamedInt) and isinstance(other, NamedInt): - return NamedInt( - '(%s + %s)' % (self.__name__, other.__name__), - temp ) - else: - return temp - - class NEI(NamedInt, Enum): - __qualname__ = 'NEI' # needed for pickle protocol 4 - x = ('the-x', 1) - y = ('the-y', 2) - - - self.assertTrue(NEI.__new__ is Enum.__new__) - self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") - globals()['NamedInt'] = NamedInt - globals()['NEI'] = NEI - NI5 = NamedInt('test', 5) - self.assertEqual(NI5, 5) - test_pickle_dump_load(self.assertEqual, NI5, 5) - self.assertEqual(NEI.y.value, 2) - test_pickle_dump_load(self.assertTrue, NEI.y) - - def test_subclasses_with_reduce_ex(self): - class NamedInt(int): - __qualname__ = 'NamedInt' # needed for pickle protocol 4 - def __new__(cls, *args): - _args = args - if len(args) < 1: - raise TypeError("name and value must be specified") - name, args = args[0], args[1:] - self = int.__new__(cls, *args) - self._intname = name - self._args = _args - return self - def __reduce_ex__(self, proto): - return self.__class__, self._args - @property - def __name__(self): - return self._intname - def __repr__(self): - # repr() is updated to include the name and type info - return "%s(%r, %s)" % (type(self).__name__, - self.__name__, - int.__repr__(self)) - def __str__(self): - # str() is unchanged, even if it relies on the repr() fallback - base = int - base_str = base.__str__ - if base_str.__objclass__ is object: - return base.__repr__(self) - return base_str(self) - # for simplicity, we only define one operator that - # propagates expressions - def __add__(self, other): - temp = int(self) + int( other) - if isinstance(self, NamedInt) and isinstance(other, NamedInt): - return NamedInt( - '(%s + %s)' % (self.__name__, other.__name__), - temp ) - else: - return temp - - class NEI(NamedInt, Enum): - __qualname__ = 'NEI' # needed for pickle protocol 4 - x = ('the-x', 1) - y = ('the-y', 2) - - - self.assertTrue(NEI.__new__ is Enum.__new__) - self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") - globals()['NamedInt'] = NamedInt - globals()['NEI'] = NEI - NI5 = NamedInt('test', 5) - self.assertEqual(NI5, 5) - test_pickle_dump_load(self.assertEqual, NI5, 5) - self.assertEqual(NEI.y.value, 2) - test_pickle_dump_load(self.assertTrue, NEI.y) - - def test_subclasses_without_direct_pickle_support(self): - class NamedInt(int): - __qualname__ = 'NamedInt' - def __new__(cls, *args): - _args = args - name, args = args[0], args[1:] - if len(args) == 0: - raise TypeError("name and value must be specified") - self = int.__new__(cls, *args) - self._intname = name - self._args = _args - return self - @property - def __name__(self): - return self._intname - def __repr__(self): - # repr() is updated to include the name and type info - return "%s(%r, %s)" % (type(self).__name__, - self.__name__, - int.__repr__(self)) - def __str__(self): - # str() is unchanged, even if it relies on the repr() fallback - base = int - base_str = base.__str__ - if base_str.__objclass__ is object: - return base.__repr__(self) - return base_str(self) - # for simplicity, we only define one operator that - # propagates expressions - def __add__(self, other): - temp = int(self) + int( other) - if isinstance(self, NamedInt) and isinstance(other, NamedInt): - return NamedInt( - '(%s + %s)' % (self.__name__, other.__name__), - temp ) - else: - return temp - - class NEI(NamedInt, Enum): - __qualname__ = 'NEI' - x = ('the-x', 1) - y = ('the-y', 2) - - self.assertTrue(NEI.__new__ is Enum.__new__) - self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") - globals()['NamedInt'] = NamedInt - globals()['NEI'] = NEI - NI5 = NamedInt('test', 5) - self.assertEqual(NI5, 5) - self.assertEqual(NEI.y.value, 2) - test_pickle_exception(self.assertRaises, TypeError, NEI.x) - test_pickle_exception(self.assertRaises, PicklingError, NEI) - - def test_subclasses_without_direct_pickle_support_using_name(self): - class NamedInt(int): - __qualname__ = 'NamedInt' - def __new__(cls, *args): - _args = args - name, args = args[0], args[1:] - if len(args) == 0: - raise TypeError("name and value must be specified") - self = int.__new__(cls, *args) - self._intname = name - self._args = _args - return self - @property - def __name__(self): - return self._intname - def __repr__(self): - # repr() is updated to include the name and type info - return "%s(%r, %s)" % (type(self).__name__, - self.__name__, - int.__repr__(self)) - def __str__(self): - # str() is unchanged, even if it relies on the repr() fallback - base = int - base_str = base.__str__ - if base_str.__objclass__ is object: - return base.__repr__(self) - return base_str(self) - # for simplicity, we only define one operator that - # propagates expressions - def __add__(self, other): - temp = int(self) + int( other) - if isinstance(self, NamedInt) and isinstance(other, NamedInt): - return NamedInt( - '(%s + %s)' % (self.__name__, other.__name__), - temp ) - else: - return temp - - class NEI(NamedInt, Enum): - __qualname__ = 'NEI' - x = ('the-x', 1) - y = ('the-y', 2) - def __reduce_ex__(self, proto): - return getattr, (self.__class__, self._name_) - - self.assertTrue(NEI.__new__ is Enum.__new__) - self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") - globals()['NamedInt'] = NamedInt - globals()['NEI'] = NEI - NI5 = NamedInt('test', 5) - self.assertEqual(NI5, 5) - self.assertEqual(NEI.y.value, 2) - test_pickle_dump_load(self.assertTrue, NEI.y) - test_pickle_dump_load(self.assertTrue, NEI) - - def test_tuple_subclass(self): - class SomeTuple(tuple, Enum): - __qualname__ = 'SomeTuple' - first = (1, 'for the money') - second = (2, 'for the show') - third = (3, 'for the music') - self.assertTrue(type(SomeTuple.first) is SomeTuple) - self.assertTrue(isinstance(SomeTuple.second, tuple)) - self.assertEqual(SomeTuple.third, (3, 'for the music')) - globals()['SomeTuple'] = SomeTuple - test_pickle_dump_load(self.assertTrue, SomeTuple.first) - - # def test_duplicate_values_give_unique_enum_items(self): - # class NumericEnum(AutoNumberEnum): - # __order__ = 'enum_m enum_d enum_y' - # enum_m = () - # enum_d = () - # enum_y = () - # def __int__(self): - # return int(self._value_) - # self.assertEqual(int(NumericEnum.enum_d), 2) - # self.assertEqual(NumericEnum.enum_y.value, 3) - # self.assertTrue(NumericEnum(1) is NumericEnum.enum_m) - # self.assertEqual( - # list(NumericEnum), - # [NumericEnum.enum_m, NumericEnum.enum_d, NumericEnum.enum_y], - # ) - - def test_inherited_new_from_enhanced_enum(self): - class AutoNumber2(Enum): - def __new__(cls): - value = len(cls.__members__) + 1 - obj = object.__new__(cls) - obj._value_ = value - return obj - def __int__(self): - return int(self._value_) - class Color(AutoNumber2): - __order__ = 'red green blue' - red = () - green = () - blue = () - self.assertEqual(len(Color), 3, "wrong number of elements: %d (should be %d)" % (len(Color), 3)) - self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) - if PY3: - self.assertEqual(list(map(int, Color)), [1, 2, 3]) - - def test_inherited_new_from_mixed_enum(self): - class AutoNumber3(IntEnum): - def __new__(cls): - value = len(cls.__members__) + 11 - obj = int.__new__(cls, value) - obj._value_ = value - return obj - class Color(AutoNumber3): - __order__ = 'red green blue' - red = () - green = () - blue = () - self.assertEqual(len(Color), 3, "wrong number of elements: %d (should be %d)" % (len(Color), 3)) - Color.red - Color.green - Color.blue - self.assertEqual(Color.blue, 13) - - def test_equality(self): - class AlwaysEqual: - def __eq__(self, other): - return True - class OrdinaryEnum(Enum): - a = 1 - self.assertEqual(AlwaysEqual(), OrdinaryEnum.a) - self.assertEqual(OrdinaryEnum.a, AlwaysEqual()) - - def test_ordered_mixin(self): - class Grade(OrderedEnum): - __order__ = 'A B C D F' - A = 5 - B = 4 - C = 3 - D = 2 - F = 1 - self.assertEqual(list(Grade), [Grade.A, Grade.B, Grade.C, Grade.D, Grade.F]) - self.assertTrue(Grade.A > Grade.B) - self.assertTrue(Grade.F <= Grade.C) - self.assertTrue(Grade.D < Grade.A) - self.assertTrue(Grade.B >= Grade.B) - - def test_missing_deprecated(self): - class Label(Enum): - AnyApple = 0 - RedApple = 1 - GreenApple = 2 - @classmethod - def _missing_(cls, name): - return cls.AnyApple - - self.assertEqual(Label.AnyApple, Label(4)) - with self.assertRaises(AttributeError): - Label.redapple - with self.assertRaises(KeyError): - Label['redapple'] - - def test_missing(self): - class Label(Enum): - AnyApple = 0 - RedApple = 1 - GreenApple = 2 - @classmethod - def _missing_value_(cls, value): - return cls.AnyApple - - self.assertEqual(Label.AnyApple, Label(4)) - with self.assertRaises(AttributeError): - Label.redapple - with self.assertRaises(KeyError): - Label['redapple'] - - def test_missing_name(self): - class Label(Enum): - RedApple = 1 - GreenApple = 2 - @classmethod - def _missing_name_(cls, name): - for member in cls: - if member.name.lower() == name.lower(): - return member - - Label['redapple'] - with self.assertRaises(AttributeError): - Label.redapple - with self.assertRaises(ValueError): - Label('redapple') - - def test_missing_value_bad_input(self): - class Label(Enum): - AnyApple = 0 - RedApple = 1 - GreenApple = 2 - @classmethod - def _missing_value_(cls, value): - return cls.AnyApple - - self.assertEqual(Label.AnyApple, Label(4)) - with self.assertRaises(KeyError): - Label[True] - - def test_missing_name_bad_return(self): - class Label(Enum): - RedApple = 1 - GreenApple = 2 - @classmethod - def _missing_name_(cls, name): - return None - - with self.assertRaises(AttributeError): - Label.redapple - with self.assertRaises(ValueError): - Label('redapple') - with self.assertRaises(KeyError): - Label['redapple'] - - def test_extending2(self): - def bad_extension(): - class Shade(Enum): - def shade(self): - print(self.name) - class Color(Shade): - red = 1 - green = 2 - blue = 3 - class MoreColor(Color): - cyan = 4 - magenta = 5 - yellow = 6 - self.assertRaises(TypeError, bad_extension) - - def test_extending3(self): - class Shade(Enum): - def shade(self): - return self.name - class Color(Shade): - def hex(self): - return '%s hexlified!' % self.value - class MoreColor(Color): - cyan = 4 - magenta = 5 - yellow = 6 - self.assertEqual(MoreColor.magenta.hex(), '5 hexlified!') - - def test_extending5(self): - class Color(Enum): - _order_ = 'red green blue value' - red = 1 - green = 2 - blue = 3 - value = 4 - self.assertEqual(list(Color), [Color.red, Color.green, Color.blue, Color.value]) - self.assertEqual(Color.value.name, 'value') - self.assertEqual(Color.value.value, 4) - self.assertTrue(Color.value in Color) - self.assertEqual(Color(4), Color.value) - self.assertEqual(Color['value'], Color.value) - self.assertEqual(Color.red.value, 1) - - CONTINUE = 100, 'Continue', 'Request received, please continue' - SWITCHING_PROTOCOLS = (101, 'Switching Protocols', - 'Switching to new protocol; obey Upgrade header') - PROCESSING = 102, 'Processing' - - def test_no_duplicates(self): - def bad_duplicates(): - class Color1(UniqueEnum): - red = 1 - green = 2 - blue = 3 - class Color2(UniqueEnum): - red = 1 - green = 2 - blue = 3 - grene = 2 - self.assertRaises(ValueError, bad_duplicates) - - def test_no_duplicates_kinda(self): - class Silly(UniqueEnum): - one = 1 - two = 'dos' - name = 3 - class Sillier(IntEnum, UniqueEnum): - single = 1 - name = 2 - triple = 3 - value = 4 - - def test_init(self): - class Planet(Enum): - MERCURY = (3.303e+23, 2.4397e6) - VENUS = (4.869e+24, 6.0518e6) - EARTH = (5.976e+24, 6.37814e6) - MARS = (6.421e+23, 3.3972e6) - JUPITER = (1.9e+27, 7.1492e7) - SATURN = (5.688e+26, 6.0268e7) - URANUS = (8.686e+25, 2.5559e7) - NEPTUNE = (1.024e+26, 2.4746e7) - def __init__(self, mass, radius): - self.mass = mass # in kilograms - self.radius = radius # in meters - @property - def surface_gravity(self): - # universal gravitational constant (m3 kg-1 s-2) - G = 6.67300E-11 - return G * self.mass / (self.radius * self.radius) - self.assertEqual(round(Planet.EARTH.surface_gravity, 2), 9.80) - self.assertEqual(Planet.EARTH.value, (5.976e+24, 6.37814e6)) - - def test_init_and_shadowing_attribute(self): - class SelectionEnum(str, Enum): - _init_ = 'db user' - def __new__(cls, *args, **kwds): - count = len(cls.__members__) - obj = str.__new__(cls, args[0]) - obj._count = count - obj._value_ = args - return obj - @staticmethod - def _generate_next_value_(name, start, count, values, *args, **kwds): - return (name, ) + args - class DeviceTypeSource(SelectionEnum): - _order_ = 'user system' - user = "User controlled" - system = "System controlled" - self.assertEqual(DeviceTypeSource.system.db, 'system') - self.assertEqual(DeviceTypeSource.system.user, 'System controlled') - self.assertEqual(DeviceTypeSource.user.db, 'user') - self.assertEqual(DeviceTypeSource.user.user, 'User controlled') - - def test_nonhash_value(self): - class AutoNumberInAList(Enum): - def __new__(cls): - value = [len(cls.__members__) + 1] - obj = object.__new__(cls) - obj._value_ = value - return obj - class ColorInAList(AutoNumberInAList): - __order__ = 'red green blue' - red = () - green = () - blue = () - self.assertEqual(list(ColorInAList), [ColorInAList.red, ColorInAList.green, ColorInAList.blue]) - self.assertEqual(ColorInAList.red.value, [1]) - self.assertEqual(ColorInAList([1]), ColorInAList.red) - - def test_number_reset_and_order_cleanup(self): - class Confused(Enum): - _order_ = 'ONE TWO THREE UNO DOS TRES FOUR' - ONE = auto() - TWO = auto() - THREE = auto() - UNO = 1 - DOS = auto() - TRES = auto() - FOUR = auto() - self.assertEqual(list(Confused), [Confused.ONE, Confused.TWO, Confused.THREE, Confused.FOUR]) - self.assertIs(Confused.TWO, Confused.DOS) - self.assertEqual(Confused.DOS._value_, 2) - self.assertEqual(Confused.TRES._value_, 3) - self.assertEqual(Confused.FOUR._value_, 4) - - def test_conflicting_types_resolved_in_new(self): - class LabelledIntEnum(int, Enum): - def __new__(cls, *args): - value, label = args - obj = int.__new__(cls, value) - obj.label = label - obj._value_ = value - return obj - - class LabelledList(LabelledIntEnum): - unprocessed = (1, "Unprocessed") - payment_complete = (2, "Payment Complete") - - self.assertEqual(LabelledList.unprocessed, 1) - self.assertEqual(LabelledList(1), LabelledList.unprocessed) - self.assertEqual(list(LabelledList), [LabelledList.unprocessed, LabelledList.payment_complete]) - - def test_auto_number(self): - class Color(Enum): - _order_ = 'red blue green' - red = auto() - blue = auto() - green = auto() - - self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) - self.assertEqual(Color.red.value, 1) - self.assertEqual(Color.blue.value, 2) - self.assertEqual(Color.green.value, 3) - - def test_auto_name(self): - class Color(Enum): - _order_ = 'red blue green' - def _generate_next_value_(name, start, count, last): - return name - red = auto() - blue = auto() - green = auto() - - self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) - self.assertEqual(Color.red.value, 'red') - self.assertEqual(Color.blue.value, 'blue') - self.assertEqual(Color.green.value, 'green') - - def test_auto_name_inherit(self): - class AutoNameEnum(Enum): - def _generate_next_value_(name, start, count, last): - return name - class Color(AutoNameEnum): - _order_ = 'red blue green' - red = auto() - blue = auto() - green = auto() - - self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) - self.assertEqual(Color.red.value, 'red') - self.assertEqual(Color.blue.value, 'blue') - self.assertEqual(Color.green.value, 'green') - - def test_auto_garbage(self): - class Color(Enum): - _order_ = 'red blue' - red = 'red' - blue = auto() - self.assertEqual(Color.blue.value, 1) - - def test_auto_garbage_corrected(self): - class Color(Enum): - _order_ = 'red blue green' - red = 'red' - blue = 2 - green = auto() - - self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) - self.assertEqual(Color.red.value, 'red') - self.assertEqual(Color.blue.value, 2) - self.assertEqual(Color.green.value, 3) - - def test_duplicate_auto(self): - # - class MoreDupes(Enum): - _order_ = 'A B C' - A = auto() - B = A, - C = auto() - self.assertEqual(list(MoreDupes), [MoreDupes.A, MoreDupes.B, MoreDupes.C]) - self.assertEqual([m.value for m in MoreDupes], [1, (1, ), 2]) - # - class Dupes(Enum): - _order_ = 'first second third' - first = primero = auto() - second = auto() - third = auto() - self.assertEqual([Dupes.first, Dupes.second, Dupes.third], list(Dupes)) - - def test_auto_value_with_auto(self): - - class SelectionEnum(Enum): - _init_ = 'db user' - def __new__(cls, *args, **kwds): - count = len(cls.__members__) - obj = object.__new__(cls) - obj._count = count - obj._value_ = args - obj.db, obj.user = args - return obj - @staticmethod - def _generate_next_value_(name, start, count, values, *args, **kwds): - return (name, ) + args - - class Test(SelectionEnum): - _order_ = 'this that' - this = auto('these') - that = auto('those') - - self.assertEqual(list(Test), [Test.this, Test.that]) - self.assertEqual(Test.this.name, 'this') - self.assertEqual(Test.this.value, ('this', 'these')) - self.assertEqual(Test.this.db, 'this') - self.assertEqual(Test.this.user, 'these') - self.assertEqual(Test.that.name, 'that') - self.assertEqual(Test.that.value, ('that', 'those')) - self.assertEqual(Test.that.db, 'that') - self.assertEqual(Test.that.user, 'those') - - def test_auto_value_with_autovalue(self): - - class SelectionEnum(Enum): - _init_ = 'db user' - def __new__(cls, *args, **kwds): - count = len(cls.__members__) - obj = object.__new__(cls) - obj._count = count - obj._value_ = args - return obj - @staticmethod - def _generate_next_value_(name, start, count, values, *args, **kwds): - return (name, ) + args - - class Test(SelectionEnum): - _order_ = 'this that' - this = 'these' - that = 'those' - - self.assertEqual(list(Test), [Test.this, Test.that]) - self.assertEqual(Test.this.name, 'this') - self.assertEqual(Test.this.value, ('this', 'these')) - self.assertEqual(Test.this.db, 'this') - self.assertEqual(Test.this.user, 'these') - self.assertEqual(Test.that.name, 'that') - self.assertEqual(Test.that.value, ('that', 'those')) - self.assertEqual(Test.that.db, 'that') - self.assertEqual(Test.that.user, 'those') - - def test_auto_and_kwds(self): - class Item(Enum): - _order_ = 'A B' - A = auto(size=100, req={'red': True}) - B = auto(size=200, req={'red': False}) - # - def __new__(cls, value, size, req): - obj = object.__new__(cls) - obj._value_ = value - obj.size = size - obj.req= req - return obj - self.assertEqual((Item.A.value, Item.A.size, Item.A.req), (1, 100, {'red': True})) - self.assertEqual((Item.B.value, Item.B.size, Item.B.req), (2, 200, {'red': False})) - - def test_empty_with_functional_api(self): - empty = aenum.IntEnum('Foo', {}) - self.assertEqual(len(empty), 0) - - def test_auto_init(self): - class Planet(Enum): - _init_ = 'mass radius' - MERCURY = (3.303e+23, 2.4397e6) - VENUS = (4.869e+24, 6.0518e6) - EARTH = (5.976e+24, 6.37814e6) - MARS = (6.421e+23, 3.3972e6) - JUPITER = (1.9e+27, 7.1492e7) - SATURN = (5.688e+26, 6.0268e7) - URANUS = (8.686e+25, 2.5559e7) - NEPTUNE = (1.024e+26, 2.4746e7) - @property - def surface_gravity(self): - # universal gravitational constant (m3 kg-1 s-2) - G = 6.67300E-11 - return G * self.mass / (self.radius * self.radius) - self.assertEqual(round(Planet.EARTH.surface_gravity, 2), 9.80) - self.assertEqual(Planet.EARTH.value, (5.976e+24, 6.37814e6)) - - def test_auto_init_with_value(self): - class Color(Enum): - _init_='value, rgb' - RED = 1, (1, 0, 0) - BLUE = 2, (0, 1, 0) - GREEN = 3, (0, 0, 1) - self.assertEqual(Color.RED.value, 1) - self.assertEqual(Color.BLUE.value, 2) - self.assertEqual(Color.GREEN.value, 3) - self.assertEqual(Color.RED.rgb, (1, 0, 0)) - self.assertEqual(Color.BLUE.rgb, (0, 1, 0)) - self.assertEqual(Color.GREEN.rgb, (0, 0, 1)) - - def test_noalias(self): - class Settings(Enum): - _settings_ = NoAlias - red = 1 - rojo = 1 - self.assertFalse(Settings.red is Settings.rojo) - self.assertRaises(TypeError, Settings, 1) - - def test_auto_and_init(self): - class Field(int, Enum): - _order_ = 'TYPE START' - _init_ = 'value __doc__' - TYPE = "Char, Date, Logical, etc." - START = "Field offset in record" - self.assertEqual(Field.TYPE, 1) - self.assertEqual(Field.START, 2) - self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.') - self.assertEqual(Field.START.__doc__, 'Field offset in record') - - def test_auto_and_start(self): - class Field(IntEnum): - _order_ = 'TYPE START' - _start_ = 0 - _init_ = 'value __doc__' - TYPE = "Char, Date, Logical, etc." - START = "Field offset in record" - self.assertEqual(Field.TYPE, 0) - self.assertEqual(Field.START, 1) - self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.') - self.assertEqual(Field.START.__doc__, 'Field offset in record') - - def test_auto_and_init_and_some_values(self): - class Field(int, Enum): - _order_ = 'TYPE START BLAH BELCH' - _init_ = 'value __doc__' - TYPE = "Char, Date, Logical, etc." - START = "Field offset in record" - BLAH = 5, "test blah" - BELCH = 'test belch' - self.assertEqual(Field.TYPE, 1) - self.assertEqual(Field.START, 2) - self.assertEqual(Field.BLAH, 5) - self.assertEqual(Field.BELCH, 6) - self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.') - self.assertEqual(Field.START.__doc__, 'Field offset in record') - self.assertEqual(Field.BLAH.__doc__, 'test blah') - self.assertEqual(Field.BELCH.__doc__, 'test belch') - - def test_auto_and_init_w_value_and_too_many_values(self): - with self.assertRaisesRegex(TypeError, r'Field\.BLAH: number of fields provided do not match init'): - class Field(int, Enum): - _order_ = 'TYPE START BLAH BELCH' - _init_ = 'value __doc__' - TYPE = 1, "Char, Date, Logical, etc." - START = 2, "Field offset in record" - BLAH = 5, 6, "test blah" - BELCH = 7, 'test belch' - - def test_auto_and_init_and_some_complex_values(self): - class Field(int, Enum): - _order_ = 'TYPE START BLAH BELCH' - _init_ = 'value __doc__ help' - TYPE = "Char, Date, Logical, etc.", "fields composed of character data" - START = "Field offset in record", "where the data starts in the record" - BLAH = 5, "test blah", "some help" - BELCH = 'test belch', "some more help" - self.assertEqual(Field.TYPE, 1) - self.assertEqual(Field.START, 2) - self.assertEqual(Field.BLAH, 5) - self.assertEqual(Field.BELCH, 6) - self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.') - self.assertEqual(Field.START.__doc__, 'Field offset in record') - self.assertEqual(Field.BLAH.__doc__, 'test blah') - self.assertEqual(Field.BELCH.__doc__, 'test belch') - self.assertEqual(Field.TYPE.help, "fields composed of character data") - self.assertEqual(Field.START.help, "where the data starts in the record") - self.assertEqual(Field.BLAH.help, "some help") - self.assertEqual(Field.BELCH.help, "some more help") - - def test_auto_and_init_inherited(self): - class AutoEnum(IntEnum): - _start_ = 0 - _init_ = 'value __doc__' - class Field(AutoEnum): - _order_ = 'TYPE START BLAH BELCH' - TYPE = "Char, Date, Logical, etc." - START = "Field offset in record" - BLAH = 5, "test blah" - BELCH = 'test belch' - self.assertEqual(Field.TYPE, 0) - self.assertEqual(Field.START, 1) - self.assertEqual(Field.BLAH, 5) - self.assertEqual(Field.BELCH, 6) - self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.') - self.assertEqual(Field.START.__doc__, 'Field offset in record') - self.assertEqual(Field.BLAH.__doc__, 'test blah') - self.assertEqual(Field.BELCH.__doc__, 'test belch') - - def test_missing_value_error(self): - with self.assertRaisesRegex(TypeError, r"_value_ not set in __new__"): - class Combined(str, Enum): - # - _init_ = 'value sequence' - _order_ = lambda m: m.sequence - # - def __new__(cls, value, *args): - enum = str.__new__(cls, value) - if '(' in value: - fis_name, segment = value.split('(', 1) - segment = segment.strip(' )') - else: - fis_name = value - segment = None - enum.fis_name = fis_name - enum.segment = segment - return enum - # - def __repr__(self): - return "<%s.%s>" % (self.__class__.__name__, self._name_) - # - key_type = 'An$(1,2)', 0 - company_id = 'An$(3,2)', 1 - code = 'An$(5,1)', 2 - description = 'Bn$', 3 - - - def test_auto_and_enum(self): - class Foo(aenum.Flag): - _order_ = 'a b c' - a = aenum.auto() - b = a | aenum.auto() - c = 2 - - self.assertEqual([Foo.a, Foo.c], list(Foo)) - self.assertEqual(Foo.a.value, 1) - self.assertEqual(Foo.b.value, 3) - - def test_multiple_arg_auto(self): - class AutoName(Enum): - def _generate_next_value_(name, start, count, last, *args, **kwds): - return (name, ) + args - # - class Planet(AutoName): - _init_ = 'value mass radius' - MERCURY = auto(3.303e+23, 2.4397e6) - VENUS = auto(4.869e+24, 6.0518e6) - self.assertEqual(Planet.MERCURY.value, 'MERCURY') - - def test_auto_w_multiple_arg(self): - class AutoName(Enum): - def _generate_next_value_(name, start, count, last, *args, **kwds): - return (name, ) + args - # - class Planet(AutoName): - _init_ = 'value mass radius' - MERCURY = auto(), 3.303e+23, 2.4397e6 # doesn't work - VENUS = auto(), 4.869e+24, 6.0518e6 # doesn't work - self.assertEqual(Planet.MERCURY.value, 'MERCURY') - - def test_auto_gnv_and_init(self): - class AutoName(Enum): - def _generate_next_value_(name, start, count, last, *args, **kwds): - return (name, ) + args - # - class Planet(AutoName): - _init_ = 'value mass radius' - MERCURY = 3.303e+23, 2.4397e6 # doesn't work - VENUS = 4.869e+24, 6.0518e6 # doesn't work - self.assertEqual(Planet.MERCURY.value, 'MERCURY') - - # def test_AutoNumberEnum_and_property(self): - # class Color(aenum.AutoNumberEnum): - # red = () - # green = () - # blue = () - # @property - # def cap_name(self): - # return self.name.title() - # self.assertEqual(Color.blue.cap_name, 'Blue') - - # def test_AutoNumberEnum(self): - # class Color(aenum.AutoNumberEnum): - # _order_ = 'red green blue' - # red = () - # green = () - # blue = () - # self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) - # self.assertEqual(Color.red.value, 1) - # self.assertEqual(Color.green.value, 2) - # self.assertEqual(Color.blue.value, 3) - - def test_MultiValue_with_init_wo_value(self): - class Color(Enum): - _init_ = 'color r g b' - _order_ = 'red green blue' - _settings_ = MultiValue - red = 'red', 1, 2, 3 - green = 'green', 4, 5, 6 - blue = 'blue', 7, 8, 9 - self.assertEqual(Color.red.value, 'red') - self.assertEqual(Color.red.color, 'red') - self.assertEqual(Color.red.r, 1) - self.assertEqual(Color.red.g, 2) - self.assertEqual(Color.red.b, 3) - self.assertEqual(Color.green.value, 'green') - self.assertEqual(Color.green.color, 'green') - self.assertEqual(Color.green.r, 4) - self.assertEqual(Color.green.g, 5) - self.assertEqual(Color.green.b, 6) - self.assertEqual(Color.blue.value, 'blue') - self.assertEqual(Color.blue.color, 'blue') - self.assertEqual(Color.blue.r, 7) - self.assertEqual(Color.blue.g, 8) - self.assertEqual(Color.blue.b, 9) - self.assertIs(Color('red'), Color.red) - self.assertIs(Color(1), Color.red) - self.assertIs(Color(2), Color.red) - self.assertIs(Color(3), Color.red) - self.assertIs(Color('green'), Color.green) - self.assertIs(Color(4), Color.green) - self.assertIs(Color(5), Color.green) - self.assertIs(Color(6), Color.green) - self.assertIs(Color('blue'), Color.blue) - self.assertIs(Color(7), Color.blue) - self.assertIs(Color(8), Color.blue) - self.assertIs(Color(9), Color.blue) - - def test_MultiValue_with_init_w_value(self): - class Color(Enum): - _init_ = 'value r g b' - _order_ = 'red green blue' - _settings_ = MultiValue - red = 'red', 1, 2, 3 - green = 'green', 4, 5, 6 - blue = 'blue', 7, 8, 9 - self.assertEqual(Color.red.value, 'red') - self.assertEqual(Color.red.r, 1) - self.assertEqual(Color.red.g, 2) - self.assertEqual(Color.red.b, 3) - self.assertEqual(Color.green.value, 'green') - self.assertEqual(Color.green.r, 4) - self.assertEqual(Color.green.g, 5) - self.assertEqual(Color.green.b, 6) - self.assertEqual(Color.blue.value, 'blue') - self.assertEqual(Color.blue.r, 7) - self.assertEqual(Color.blue.g, 8) - self.assertEqual(Color.blue.b, 9) - self.assertIs(Color('red'), Color.red) - self.assertIs(Color(1), Color.red) - self.assertIs(Color(2), Color.red) - self.assertIs(Color(3), Color.red) - self.assertIs(Color('green'), Color.green) - self.assertIs(Color(4), Color.green) - self.assertIs(Color(5), Color.green) - self.assertIs(Color(6), Color.green) - self.assertIs(Color('blue'), Color.blue) - self.assertIs(Color(7), Color.blue) - self.assertIs(Color(8), Color.blue) - self.assertIs(Color(9), Color.blue) - - def test_MultiValue_with_init_wo_value_w_autonumber(self): - class Color(AutoNumberEnum): - _init_ = 'color r g b' - _order_ = 'red green blue' - _settings_ = MultiValue - red = 'red', 10, 20, 30 - green = 'green', 40, 50, 60 - blue = 'blue', 70, 80, 90 - self.assertEqual(Color.red.value, 1) - self.assertEqual(Color.red.color, 'red') - self.assertEqual(Color.red.r, 10) - self.assertEqual(Color.red.g, 20) - self.assertEqual(Color.red.b, 30) - self.assertEqual(Color.green.value, 2) - self.assertEqual(Color.green.color, 'green') - self.assertEqual(Color.green.r, 40) - self.assertEqual(Color.green.g, 50) - self.assertEqual(Color.green.b, 60) - self.assertEqual(Color.blue.value, 3) - self.assertEqual(Color.blue.color, 'blue') - self.assertEqual(Color.blue.r, 70) - self.assertEqual(Color.blue.g, 80) - self.assertEqual(Color.blue.b, 90) - self.assertIs(Color(1), Color.red) - self.assertIs(Color('red'), Color.red) - self.assertIs(Color(10), Color.red) - self.assertIs(Color(20), Color.red) - self.assertIs(Color(30), Color.red) - self.assertIs(Color(2), Color.green) - self.assertIs(Color('green'), Color.green) - self.assertIs(Color(40), Color.green) - self.assertIs(Color(50), Color.green) - self.assertIs(Color(60), Color.green) - self.assertIs(Color(3), Color.blue) - self.assertIs(Color('blue'), Color.blue) - self.assertIs(Color(70), Color.blue) - self.assertIs(Color(80), Color.blue) - self.assertIs(Color(90), Color.blue) - - def test_multivalue_and_autonumber_wo_init_wo_value(self): - class Day(Enum): - _settings_ = MultiValue, AddValue - _order_ = 'one two three' - _start_ = 7 - one = "21", "one" - two = "22", "two" - three = "23", "three" - self.assertEqual(Day.one.value, 7) - self.assertEqual(Day.two.value, 8) - self.assertEqual(Day.three.value, 9) - self.assertEqual(Day('21'), Day.one) - self.assertEqual(Day('one'), Day.one) - - def test_multivalue_and_autonumber_wo_init_w_some_value(self): - class Color(Enum): - _settings_ = MultiValue, Unique - _order_ = 'BLACK RED BLUE YELLOW GREEN MAGENTA' - _init_ = "value description" - BLACK = -1, "Text0" - RED = -50, "Text1" - BLUE = auto(), "Text2" - YELLOW = auto(), "Text3" - GREEN = -70, "Text4" - MAGENTA = auto(), "Text5" - self.assertEqual(Color.BLACK.value, -1) - self.assertEqual(Color.RED.value, -50) - self.assertEqual(Color.BLUE.value, -49) - self.assertEqual(Color.YELLOW.value, -48) - self.assertEqual(Color.GREEN.value, -70) - self.assertEqual(Color.MAGENTA.value, -69) - self.assertEqual(Color(-1), Color.BLACK) - self.assertEqual(Color('Text2'), Color.BLUE) - - def test_combine_new_settings_with_old_settings(self): - class Auto(Enum): - _settings_ = Unique - with self.assertRaises(ValueError): - class AutoUnique(Auto): - BLAH = auto() - BLUH = auto() - ICK = 1 - - def test_timedelta(self): - class Period(timedelta, Enum): - ''' - different lengths of time - ''' - _init_ = 'value period' - _settings_ = NoAlias - _ignore_ = 'Period i' - Period = vars() - for i in range(31): - Period['day_%d' % i] = i, 'day' - for i in range(15): - Period['week_%d' % i] = i*7, 'week' - for i in range(12): - Period['month_%d' % i] = i*30, 'month' - OneDay = day_1 - OneWeek = week_1 - self.assertFalse(hasattr(Period, '_ignore_')) - self.assertFalse(hasattr(Period, 'Period')) - self.assertFalse(hasattr(Period, 'i')) - self.assertTrue(isinstance(Period.day_1, timedelta)) - - def test_skip(self): - class enumA(Enum): - @skip - class enumB(Enum): - elementA = 'a' - elementB = 'b' - @skip - class enumC(Enum): - elementC = 'c' - elementD = 'd' - self.assertIs(enumA.enumB, enumA.__dict__['enumB']) - - def test_nonmember(self): - class enumA(Enum): - @nonmember - class enumB(Enum): - elementA = 'a' - elementB = 'b' - @nonmember - class enumC(Enum): - elementC = 'c' - elementD = 'd' - self.assertIs(enumA.enumB, enumA.__dict__['enumB']) - - def test_member_with_external_functions(self): - class Func(Enum): - _order_ = 'an_int a_str' - an_int = member(int) - a_str = member(str) - @classproperty - def types(cls): - return [m.value for m in list(cls)] - def __repr__(self): - return "<%s.%s>" % (self.__class__.__name__, self.name, ) - def __call__(self, *args, **kwds): - return self.value(*args, **kwds) - # - self.assertEqual([Func.an_int, Func.a_str], list(Func)) - self.assertEqual([int, str], Func.types) - self.assertEqual(Func.an_int(7), 7) - self.assertEqual(Func.a_str('BlahBlah'), 'BlahBlah') - - def test_member_with_internal_functions(self): - class Func(Enum): - _order_ = 'haha hehe' - @member - def haha(): - return 'haha' - @member - def hehe(name): - return 'hehe -- what a name! %s!' % name - @classproperty - def types(cls): - return [m.value for m in list(cls)] - def __repr__(self): - return "<%s.%s>" % (self.__class__.__name__, self.name, ) - def __call__(self, *args, **kwds): - return self.value(*args, **kwds) - # - self.assertEqual([Func.haha, Func.hehe], list(Func)) - self.assertEqual([Func.haha.value, Func.hehe.value], Func.types) - self.assertEqual(Func.haha(), 'haha') - self.assertEqual(Func.hehe('BlahBlah'), 'hehe -- what a name! BlahBlah!') - - def test_constantness_of_constants(self): - class Universe(Enum): - PI = constant(3.141596) - G = constant(6.67300E-11) - self.assertEqual(Universe.PI, 3.141596) - self.assertRaisesRegex(AttributeError, r'cannot rebind constant', setattr, Universe, 'PI', 9) - self.assertRaisesRegex(AttributeError, r'cannot delete constant', delattr, Universe, 'PI') - - def test_math_and_stuff_with_constants(self): - class Universe(Enum): - PI = constant(3.141596) - TAU = constant(2 * PI) - self.assertEqual(Universe.PI, 3.141596) - self.assertEqual(Universe.TAU, 2 * Universe.PI) - - def test_constant_with_auto_is_updated(self): - class Fruit(Flag): - _order_ = 'apple banana lemon orange' - apple = auto() - banana = auto() - lemon = auto() - orange = auto() - CitrusTypes = constant(lemon | orange) - self.assertEqual(list(Fruit), [Fruit.apple, Fruit.banana, Fruit.lemon, Fruit.orange]) - self.assertEqual(list(Fruit.CitrusTypes), [Fruit.lemon, Fruit.orange]) - self.assertTrue(Fruit.orange in Fruit.CitrusTypes) - - - def test_order_as_function(self): - # first with _init_ - class TestSequence(Enum): - _init_ = 'value, sequence' - _order_ = lambda member: member.sequence - item_id = 'An$(1,6)', 0 # Item Code - company_id = 'An$(7,2)', 1 # Company Code - warehouse_no = 'An$(9,4)', 2 # Warehouse Number - company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY - key_type = 'Cn$(19,3)', 4 # Key Type = '1**' - available = 'Zn$(1,1)', 5 # Available? - contract_item = 'Bn(2,1)', 6 # Contract Item? - sales_category = 'Fn', 7 # Sales Category - gl_category = 'Rn$(5,1)', 8 # G/L Category - warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category - inv_units = 'Qn$(7,2)', 10 # Inv Units - for i, member in enumerate(TestSequence): - self.assertEqual(i, member.sequence) - ts = TestSequence - self.assertEqual(ts.item_id.name, 'item_id') - self.assertEqual(ts.item_id.value, 'An$(1,6)') - self.assertEqual(ts.item_id.sequence, 0) - self.assertEqual(ts.company_id.name, 'company_id') - self.assertEqual(ts.company_id.value, 'An$(7,2)') - self.assertEqual(ts.company_id.sequence, 1) - self.assertEqual(ts.warehouse_no.name, 'warehouse_no') - self.assertEqual(ts.warehouse_no.value, 'An$(9,4)') - self.assertEqual(ts.warehouse_no.sequence, 2) - self.assertEqual(ts.company.name, 'company') - self.assertEqual(ts.company.value, 'Hn$(13,6)') - self.assertEqual(ts.company.sequence, 3) - self.assertEqual(ts.key_type.name, 'key_type') - self.assertEqual(ts.key_type.value, 'Cn$(19,3)') - self.assertEqual(ts.key_type.sequence, 4) - self.assertEqual(ts.available.name, 'available') - self.assertEqual(ts.available.value, 'Zn$(1,1)') - self.assertEqual(ts.available.sequence, 5) - self.assertEqual(ts.contract_item.name, 'contract_item') - self.assertEqual(ts.contract_item.value, 'Bn(2,1)') - self.assertEqual(ts.contract_item.sequence, 6) - self.assertEqual(ts.sales_category.name, 'sales_category') - self.assertEqual(ts.sales_category.value, 'Fn') - self.assertEqual(ts.sales_category.sequence, 7) - self.assertEqual(ts.gl_category.name, 'gl_category') - self.assertEqual(ts.gl_category.value, 'Rn$(5,1)') - self.assertEqual(ts.gl_category.sequence, 8) - self.assertEqual(ts.warehouse_category.name, 'warehouse_category') - self.assertEqual(ts.warehouse_category.value, 'Sn$(6,1)') - self.assertEqual(ts.warehouse_category.sequence, 9) - self.assertEqual(ts.inv_units.name, 'inv_units') - self.assertEqual(ts.inv_units.value, 'Qn$(7,2)') - self.assertEqual(ts.inv_units.sequence, 10) - # and then without - class TestSequence(Enum): - _order_ = lambda member: member.value[1] - item_id = 'An$(1,6)', 0 # Item Code - company_id = 'An$(7,2)', 1 # Company Code - warehouse_no = 'An$(9,4)', 2 # Warehouse Number - company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY - key_type = 'Cn$(19,3)', 4 # Key Type = '1**' - available = 'Zn$(1,1)', 5 # Available? - contract_item = 'Bn(2,1)', 6 # Contract Item? - sales_category = 'Fn', 7 # Sales Category - gl_category = 'Rn$(5,1)', 8 # G/L Category - warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category - inv_units = 'Qn$(7,2)', 10 # Inv Units - for i, member in enumerate(TestSequence): - self.assertEqual(i, member.value[1]) - ts = TestSequence - self.assertEqual(ts.item_id.name, 'item_id') - self.assertEqual(ts.item_id.value, ('An$(1,6)', 0)) - self.assertEqual(ts.company_id.name, 'company_id') - self.assertEqual(ts.company_id.value, ('An$(7,2)', 1)) - self.assertEqual(ts.warehouse_no.name, 'warehouse_no') - self.assertEqual(ts.warehouse_no.value, ('An$(9,4)', 2)) - self.assertEqual(ts.company.name, 'company') - self.assertEqual(ts.company.value, ('Hn$(13,6)', 3)) - self.assertEqual(ts.key_type.name, 'key_type') - self.assertEqual(ts.key_type.value, ('Cn$(19,3)', 4)) - self.assertEqual(ts.available.name, 'available') - self.assertEqual(ts.available.value, ('Zn$(1,1)', 5)) - self.assertEqual(ts.contract_item.name, 'contract_item') - self.assertEqual(ts.contract_item.value, ('Bn(2,1)', 6)) - self.assertEqual(ts.sales_category.name, 'sales_category') - self.assertEqual(ts.sales_category.value, ('Fn', 7)) - self.assertEqual(ts.gl_category.name, 'gl_category') - self.assertEqual(ts.gl_category.value, ('Rn$(5,1)', 8)) - self.assertEqual(ts.warehouse_category.name, 'warehouse_category') - self.assertEqual(ts.warehouse_category.value, ('Sn$(6,1)', 9)) - self.assertEqual(ts.inv_units.name, 'inv_units') - self.assertEqual(ts.inv_units.value, ('Qn$(7,2)', 10)) - # then with _init_ but without value - with self.assertRaises(TypeError): - class TestSequence(Enum): - _init_ = 'sequence' - _order_ = lambda member: member.sequence - item_id = 'An$(1,6)', 0 # Item Code - company_id = 'An$(7,2)', 1 # Company Code - warehouse_no = 'An$(9,4)', 2 # Warehouse Number - company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY - key_type = 'Cn$(19,3)', 4 # Key Type = '1**' - available = 'Zn$(1,1)', 5 # Available? - contract_item = 'Bn(2,1)', 6 # Contract Item? - sales_category = 'Fn', 7 # Sales Category - gl_category = 'Rn$(5,1)', 8 # G/L Category - warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category - inv_units = 'Qn$(7,2)', 10 # Inv Units - # finally, out of order so Python 3 barfs - with self.assertRaises(TypeError): - class TestSequence(Enum): - _init_ = 'sequence' - _order_ = lambda member: member.sequence - item_id = 'An$(1,6)', 0 # Item Code - warehouse_no = 'An$(9,4)', 2 # Warehouse Number - company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY - company_id = 'An$(7,2)', 1 # Company Code - inv_units = 'Qn$(7,2)', 10 # Inv Units - available = 'Zn$(1,1)', 5 # Available? - contract_item = 'Bn(2,1)', 6 # Contract Item? - sales_category = 'Fn', 7 # Sales Category - key_type = 'Cn$(19,3)', 4 # Key Type = '1**' - gl_category = 'Rn$(5,1)', 8 # G/L Category - warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category - - def test_order_as_function_in_subclass(self): - # - class Parent(Enum): - _init_ = 'value sequence' - _order_ = lambda m: m.sequence - # - class Child(Parent): - item_id = 'An$(1,6)', 0 # Item Code - company_id = 'An$(7,2)', 1 # Company Code - warehouse_no = 'An$(9,4)', 2 # Warehouse Number - company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY - key_type = 'Cn$(19,3)', 4 # Key Type = '1**' - available = 'Zn$(1,1)', 5 # Available? - contract_item = 'Bn(2,1)', 6 # Contract Item? - sales_category = 'Fn', 7 # Sales Category - gl_category = 'Rn$(5,1)', 8 # G/L Category - warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category - inv_units = 'Qn$(7,2)', 10 # Inv Units - # - for i, member in enumerate(Child): - self.assertEqual(i, member.sequence) - # - ts = Child - self.assertEqual(ts.item_id.name, 'item_id') - self.assertEqual(ts.item_id.value, 'An$(1,6)') - self.assertEqual(ts.item_id.sequence, 0) - self.assertEqual(ts.company_id.name, 'company_id') - self.assertEqual(ts.company_id.value, 'An$(7,2)') - self.assertEqual(ts.company_id.sequence, 1) - self.assertEqual(ts.warehouse_no.name, 'warehouse_no') - self.assertEqual(ts.warehouse_no.value, 'An$(9,4)') - self.assertEqual(ts.warehouse_no.sequence, 2) - self.assertEqual(ts.company.name, 'company') - self.assertEqual(ts.company.value, 'Hn$(13,6)') - self.assertEqual(ts.company.sequence, 3) - self.assertEqual(ts.key_type.name, 'key_type') - self.assertEqual(ts.key_type.value, 'Cn$(19,3)') - self.assertEqual(ts.key_type.sequence, 4) - self.assertEqual(ts.available.name, 'available') - self.assertEqual(ts.available.value, 'Zn$(1,1)') - self.assertEqual(ts.available.sequence, 5) - self.assertEqual(ts.contract_item.name, 'contract_item') - self.assertEqual(ts.contract_item.value, 'Bn(2,1)') - self.assertEqual(ts.contract_item.sequence, 6) - self.assertEqual(ts.sales_category.name, 'sales_category') - self.assertEqual(ts.sales_category.value, 'Fn') - self.assertEqual(ts.sales_category.sequence, 7) - self.assertEqual(ts.gl_category.name, 'gl_category') - self.assertEqual(ts.gl_category.value, 'Rn$(5,1)') - self.assertEqual(ts.gl_category.sequence, 8) - self.assertEqual(ts.warehouse_category.name, 'warehouse_category') - self.assertEqual(ts.warehouse_category.value, 'Sn$(6,1)') - self.assertEqual(ts.warehouse_category.sequence, 9) - self.assertEqual(ts.inv_units.name, 'inv_units') - self.assertEqual(ts.inv_units.value, 'Qn$(7,2)') - self.assertEqual(ts.inv_units.sequence, 10) - - pass - - def test_multiple_mixin(self): - class MaxMixin(object): - @classproperty - def MAX(cls): - max = len(cls) - cls.MAX = max - return max - class StrMixin(object): - def __str__(self): - return self._name_.lower() - class SomeEnum(Enum): - def behavior(self): - return 'booyah' - class AnotherEnum(Enum): - def behavior(self): - return 'nuhuh!' - def social(self): - return "what's up?" - class Color(MaxMixin, Enum): - _order_ = 'RED GREEN BLUE' - RED = auto() - GREEN = auto() - BLUE = auto() - self.assertEqual(Color.RED.value, 1) - self.assertEqual(Color.GREEN.value, 2) - self.assertEqual(Color.BLUE.value, 3) - self.assertEqual(Color.MAX, 3) - self.assertEqual(str(Color.BLUE), 'Color.BLUE') - class Color(MaxMixin, StrMixin, Enum): - _order_ = 'RED GREEN BLUE' - RED = auto() - GREEN = auto() - BLUE = auto() - self.assertEqual(Color.RED.value, 1) - self.assertEqual(Color.GREEN.value, 2) - self.assertEqual(Color.BLUE.value, 3) - self.assertEqual(Color.MAX, 3) - self.assertEqual(str(Color.BLUE), 'blue', '%r is not %r' % (str(Color.BLUE), 'blue')) - class Color(StrMixin, MaxMixin, Enum): - _order_ = 'RED GREEN BLUE' - RED = auto() - GREEN = auto() - BLUE = auto() - self.assertEqual(Color.RED.value, 1) - self.assertEqual(Color.GREEN.value, 2) - self.assertEqual(Color.BLUE.value, 3) - self.assertEqual(Color.MAX, 3) - self.assertEqual(str(Color.BLUE), 'blue', '%r is not %r' % (str(Color.BLUE), 'blue')) - class CoolColor(StrMixin, SomeEnum, Enum): - _order_ = 'RED GREEN BLUE' - RED = auto() - GREEN = auto() - BLUE = auto() - self.assertEqual(CoolColor.RED.value, 1) - self.assertEqual(CoolColor.GREEN.value, 2) - self.assertEqual(CoolColor.BLUE.value, 3) - self.assertEqual(str(CoolColor.BLUE), 'blue', '%r is not %r' % (str(Color.BLUE), 'blue')) - self.assertEqual(CoolColor.RED.behavior(), 'booyah') - class CoolerColor(StrMixin, AnotherEnum, Enum): - _order_ = 'RED GREEN BLUE' - RED = auto() - GREEN = auto() - BLUE = auto() - self.assertEqual(CoolerColor.RED.value, 1) - self.assertEqual(CoolerColor.GREEN.value, 2) - self.assertEqual(CoolerColor.BLUE.value, 3) - self.assertEqual(str(CoolerColor.BLUE), 'blue', '%r is not %r' % (str(Color.BLUE), 'blue')) - self.assertEqual(CoolerColor.RED.behavior(), 'nuhuh!') - self.assertEqual(CoolerColor.RED.social(), "what's up?") - class CoolestColor(StrMixin, SomeEnum, AnotherEnum): - _order_ = 'RED GREEN BLUE' - RED = auto() - GREEN = auto() - BLUE = auto() - self.assertEqual(CoolestColor.RED.value, 1) - self.assertEqual(CoolestColor.GREEN.value, 2) - self.assertEqual(CoolestColor.BLUE.value, 3) - self.assertEqual(str(CoolestColor.BLUE), 'blue', '%r is not %r' % (str(Color.BLUE), 'blue')) - self.assertEqual(CoolestColor.RED.behavior(), 'booyah') - self.assertEqual(CoolestColor.RED.social(), "what's up?") - class ConfusedColor(StrMixin, AnotherEnum, SomeEnum): - _order_ = 'RED GREEN BLUE' - RED = auto() - GREEN = auto() - BLUE = auto() - self.assertEqual(ConfusedColor.RED.value, 1) - self.assertEqual(ConfusedColor.GREEN.value, 2) - self.assertEqual(ConfusedColor.BLUE.value, 3) - self.assertEqual(str(ConfusedColor.BLUE), 'blue', '%r is not %r' % (str(Color.BLUE), 'blue')) - self.assertEqual(ConfusedColor.RED.behavior(), 'nuhuh!') - self.assertEqual(ConfusedColor.RED.social(), "what's up?") - class ReformedColor(StrMixin, IntEnum, SomeEnum, AnotherEnum): - _order_ = 'RED GREEN BLUE' - RED = auto() - GREEN = auto() - BLUE = auto() - self.assertEqual(ReformedColor.RED.value, 1) - self.assertEqual(ReformedColor.GREEN.value, 2) - self.assertEqual(ReformedColor.BLUE.value, 3) - self.assertEqual(str(ReformedColor.BLUE), 'blue', '%r is not %r' % (str(Color.BLUE), 'blue')) - self.assertEqual(ReformedColor.RED.behavior(), 'booyah') - self.assertEqual(ConfusedColor.RED.social(), "what's up?") - self.assertTrue(issubclass(ReformedColor, int)) - - def test_multiple_inherited_mixin(self): - @unique - class Decision1(StrEnum): - REVERT = "REVERT" - REVERT_ALL = "REVERT_ALL" - RETRY = "RETRY" - class MyEnum(StrEnum): - pass - @unique - class Decision2(MyEnum): - REVERT = "REVERT" - REVERT_ALL = "REVERT_ALL" - RETRY = "RETRY" - - def test_value_auto_assign(self): - class Some(Enum): - def __new__(cls, val): - return object.__new__(cls) - x = 1 - y = 2 - self.assertEqual(Some.x.value, 1) - self.assertEqual(Some.y.value, 2) - - def test_enum_of_types(self): - """Support using Enum to refer to types deliberately.""" - class MyTypes(Enum): - i = int - f = float - s = str - self.assertEqual(MyTypes.i.value, int) - self.assertEqual(MyTypes.f.value, float) - self.assertEqual(MyTypes.s.value, str) - class Foo: - pass - class Bar: - pass - class MyTypes2(Enum): - a = Foo - b = Bar - self.assertEqual(MyTypes2.a.value, Foo) - self.assertEqual(MyTypes2.b.value, Bar) - class SpamEnumNotInner: - pass - class SpamEnum(Enum): - spam = SpamEnumNotInner - self.assertEqual(SpamEnum.spam.value, SpamEnumNotInner) - - if PY2: - def test_nested_classes_in_enum_do_become_members(self): - # manually set __qualname__ to remove testing framework noise - class Outer(Enum): - _order_ = 'a b Inner' - __qualname__ = "Outer" - a = 1 - b = 2 - class Inner(Enum): - __qualname__ = "Outer.Inner" - foo = 10 - bar = 11 - self.assertTrue(isinstance(Outer.Inner, Outer)) - self.assertEqual(Outer.a.value, 1) - self.assertEqual(Outer.Inner.value.foo.value, 10) - self.assertEqual( - list(Outer.Inner.value), - [Outer.Inner.value.foo, Outer.Inner.value.bar], - ) - self.assertEqual( - list(Outer), - [Outer.a, Outer.b, Outer.Inner], - ) - - def test_really_nested_classes_in_enum_do_become_members(self): - class Outer(Enum): - _order_ = 'a b Inner' - a = 1 - b = 2 - class Inner(Enum): - foo = 10 - bar = 11 - self.assertTrue(isinstance(Outer.Inner, Outer)) - self.assertEqual(Outer.a.value, 1) - self.assertEqual(Outer.Inner.value.foo.value, 10) - self.assertEqual( - list(Outer.Inner.value), - [Outer.Inner.value.foo, Outer.Inner.value.bar], - ) - self.assertEqual( - list(Outer), - [Outer.a, Outer.b, Outer.Inner], - ) - - def test_nested_classes_in_enum_are_skipped_with_skip(self): - """Support locally-defined nested classes using @skip""" - # manually set __qualname__ to remove testing framework noise - class Outer(Enum): - __qualname__ = "Outer" - a = 1 - b = 2 - @skip - class Inner(Enum): - __qualname__ = "Outer.Inner" - foo = 10 - bar = 11 - self.assertTrue(isinstance(Outer.Inner, type)) - self.assertEqual(Outer.a.value, 1) - self.assertEqual(Outer.Inner.foo.value, 10) - self.assertEqual( - list(Outer.Inner), - [Outer.Inner.foo, Outer.Inner.bar], - ) - self.assertEqual( - list(Outer), - [Outer.a, Outer.b], - ) - - def test_really_nested_classes_in_enum_are_skipped_with_skip(self): - """Support locally-defined nested classes using @skip""" - class Outer(Enum): - a = 1 - b = 2 - @skip - class Inner(Enum): - foo = 10 - bar = 11 - self.assertTrue(isinstance(Outer.Inner, type)) - self.assertEqual(Outer.a.value, 1) - self.assertEqual(Outer.Inner.foo.value, 10) - self.assertEqual( - list(Outer.Inner), - [Outer.Inner.foo, Outer.Inner.bar], - ) - self.assertEqual( - list(Outer), - [Outer.a, Outer.b], - ) - - def test_enum_call_without_arg(self): - class Color(Enum): - black = 0 - red = 1 - green = 2 - blue = 3 - # - @classmethod - def _missing_value_(cls, value): - if value is no_arg: - return cls.black - self.assertTrue(Color.red is Color(1)) - self.assertTrue(Color.black is Color()) - - def test_init_subclass(self): - class MyEnum(Enum): - def __init_subclass__(cls, **kwds): - super(MyEnum, cls).__init_subclass__(**kwds) - self.assertFalse(cls.__dict__.get('_test', False)) - cls._test1 = 'MyEnum' - # - class TheirEnum(MyEnum): - def __init_subclass__(cls, **kwds): - super(TheirEnum, cls).__init_subclass__(**kwds) - cls._test2 = 'TheirEnum' - class WhoseEnum(TheirEnum): - def __init_subclass__(cls, **kwds): - pass - class NoEnum(WhoseEnum): - ONE = 1 - self.assertEqual(TheirEnum.__dict__['_test1'], 'MyEnum') - self.assertEqual(WhoseEnum.__dict__['_test1'], 'MyEnum') - self.assertEqual(WhoseEnum.__dict__['_test2'], 'TheirEnum') - self.assertFalse(NoEnum.__dict__.get('_test1', False)) - self.assertFalse(NoEnum.__dict__.get('_test2', False)) - # - class OurEnum(MyEnum): - def __init_subclass__(cls, **kwds): - cls._test2 = 'OurEnum' - class WhereEnum(OurEnum): - def __init_subclass__(cls, **kwds): - pass - class NeverEnum(WhereEnum): - ONE = 'one' - self.assertEqual(OurEnum.__dict__['_test1'], 'MyEnum') - self.assertFalse(WhereEnum.__dict__.get('_test1', False)) - self.assertEqual(WhereEnum.__dict__['_test2'], 'OurEnum') - self.assertFalse(NeverEnum.__dict__.get('_test1', False)) - self.assertFalse(NeverEnum.__dict__.get('_test2', False)) - - -class TestStrEnum(TestCase): - - def test_set_name(self): - class Descriptor(object): - name = None - def __get__(self, instance, owner_class=None): - if instance is None: - return self - else: - return instance.__dict__[self.name] - def __set__(self, instance, value): - instance.__dict__[self.name] = value - def __set_name__(self, owner, name): - self.name = name - # - class AnEnum(Enum): - ONE = 'one' - two = Descriptor() - # - self.assertEqual(list(AnEnum), [AnEnum.ONE]) - self.assertEqual(AnEnum.two.name, 'two') - AnEnum.ONE.two = 'three' - self.assertEqual(AnEnum.ONE.two, 'three') - self.assertEqual(AnEnum.ONE.__dict__['two'], 'three') - - def test_private_names(self): - class Private(Enum): - __corporal = 'Radar' - __major_ = 'Hoolihan' - self.assertEqual(len(Private), 0) - self.assertEqual(Private._Private__corporal, 'Radar') - self.assertFalse(isinstance(Private._Private__corporal, Enum)) - self.assertEqual(Private._Private__major_, 'Hoolihan') - self.assertFalse(isinstance(Private._Private__major_, Enum)) - - def test_strenum_inherited_methods(self): - class phy(StrEnum): - pi = 'Pi' - tau = 'Tau' - self.assertTrue(phy.pi < phy.tau) - self.assertEqual(phy.pi.upper(), 'PI') - self.assertEqual(phy.tau.count('a'), 1) - - def test_strict_strenum(self): - for uhoh in (object, object(), [], Enum, 9): - with self.assertRaisesRegex(TypeError, r'values must be str'): - class Huh(StrEnum): - huh = uhoh - class Either(StrEnum): - _order_ = 'this that Those lower upper' - this = auto() - that = 'That' - Those = auto() - lower = 'lower' - upper = 'UPPER' - self.assertEqual([m.value for m in Either], ['this', 'That', 'those', 'lower', 'UPPER']) - # - with self.assertRaisesRegex(ValueError, r' is not lower-case'): - class Huh(LowerStrEnum): - huh = 'What' - # - class Lower(LowerStrEnum): - _order_ = 'this that Those lower upper' - this = auto() - that = 'that' - Those = auto() - lower = 'lower' - upper = 'upper' - self.assertEqual([m.value for m in Lower], ['this', 'that', 'those', 'lower', 'upper']) - # - with self.assertRaisesRegex(ValueError, r' is not upper-case'): - class Huh(UpperStrEnum): - huh = 'What' - # - class Upper(UpperStrEnum): - _order_ = 'this that Those lower upper' - this = auto() - that = 'THAT' - Those = auto() - lower = 'LOWER' - upper = 'UPPER' - self.assertEqual([m.value for m in Upper], ['THIS', 'THAT', 'THOSE', 'LOWER', 'UPPER']) - - def test_init_subclass(self): - class MyEnum(StrEnum): - def __init_subclass__(cls, **kwds): - super(MyEnum, cls).__init_subclass__(**kwds) - self.assertFalse(cls.__dict__.get('_test', False)) - cls._test1 = 'MyEnum' - # - class TheirEnum(MyEnum): - def __init_subclass__(cls, **kwds): - super(TheirEnum, cls).__init_subclass__(**kwds) - cls._test2 = 'TheirEnum' - class WhoseEnum(TheirEnum): - def __init_subclass__(cls, **kwds): - pass - class NoEnum(WhoseEnum): - ONE = 'one' - self.assertEqual(TheirEnum.__dict__['_test1'], 'MyEnum') - self.assertEqual(WhoseEnum.__dict__['_test1'], 'MyEnum') - self.assertEqual(WhoseEnum.__dict__['_test2'], 'TheirEnum') - self.assertFalse(NoEnum.__dict__.get('_test1', False)) - self.assertFalse(NoEnum.__dict__.get('_test2', False)) - # - class OurEnum(MyEnum): - def __init_subclass__(cls, **kwds): - cls._test2 = 'OurEnum' - class WhereEnum(OurEnum): - def __init_subclass__(cls, **kwds): - pass - class NeverEnum(WhereEnum): - ONE = 'one' - self.assertEqual(OurEnum.__dict__['_test1'], 'MyEnum') - self.assertFalse(WhereEnum.__dict__.get('_test1', False)) - self.assertEqual(WhereEnum.__dict__['_test2'], 'OurEnum') - self.assertFalse(NeverEnum.__dict__.get('_test1', False)) - self.assertFalse(NeverEnum.__dict__.get('_test2', False)) - - -class TestFlag(TestCase): - """Tests of the Flags.""" - - def setUp(self): - class Perm(Flag): - _order_ = 'R W X' - R, W, X = 4, 2, 1 - self.Perm = Perm - # - class Color(Flag): - BLACK = 0 - RED = 1 - ROJO = 1 - GREEN = 2 - BLUE = 4 - PURPLE = RED|BLUE - WHITE = RED|GREEN|BLUE - BLANCO = RED|GREEN|BLUE - self.Color = Color - # - class Fun(Flag): - _order_ = 'ONE TWO FOUR EIGHT' - ONE = auto() - TWO = auto() - THREE = ONE | TWO - FOUR = auto() - FIVE = FOUR | ONE - SIX = FOUR | TWO - SEVEN = FOUR | TWO | ONE - EIGHT = auto() - self.Fun = Fun - # - class TermColor(str, Flag): - def __new__(cls, value, code): - str_value = '\x1b[%sm' % code - obj = str.__new__(cls, str_value) - obj._value_ = value - obj.code = code - return obj - # - @classmethod - def _create_pseudo_member_values_(cls, members, *values): - code = ';'.join(m.code for m in members) - return values + (code, ) - # - AllReset = '0' # ESC [ 0 m # reset all (colors and brightness) - Bright = '1' # ESC [ 1 m # bright - Dim = '2' # ESC [ 2 m # dim (looks same as normal brightness) - Underline = '4' - Normal = '22' # ESC [ 22 m # normal brightness - # - # # FOREGROUND - 30s BACKGROUND - 40s: - FG_Black = '30' # ESC [ 30 m # black - FG_Red = '31' # ESC [ 31 m # red - FG_Green = '32' # ESC [ 32 m # green - FG_Yellow = '33' # ESC [ 33 m # yellow - FG_Blue = '34' # ESC [ 34 m # blue - FG_Magenta = '35' # ESC [ 35 m # magenta - FG_Cyan = '36' # ESC [ 36 m # cyan - FG_White = '37' # ESC [ 37 m # white - FG_Reset = '39' # ESC [ 39 m # reset - # - BG_Black = '40' # ESC [ 30 m # black - BG_Red = '41' # ESC [ 31 m # red - BG_Green = '42' # ESC [ 32 m # green - BG_Yellow = '43' # ESC [ 33 m # yellow - BG_Blue = '44' # ESC [ 34 m # blue - BG_Magenta = '45' # ESC [ 35 m # magenta - BG_Cyan = '46' # ESC [ 36 m # cyan - BG_White = '47' # ESC [ 37 m # white - BG_Reset = '49' # ESC [ 39 m # reset - # - __str__ = str.__str__ - # - def __repr__(self): - if self._name_ is not None: - return '<%s.%s>' % (self.__class__.__name__, self._name_) - else: - return '<%s: %s>' % (self.__class__.__name__, '|'.join([m.name for m in Flag.__iter__(self)])) - # - def __enter__(self): - print(self.AllReset, end='', verbose=0) - return self - # - def __exit__(self, *args): - print(self.AllReset, end='', verbose=0) - self.TermColor = TermColor - # - class Open(Flag): - RO = 0 - WO = 1 - RW = 2 - AC = 3 - CE = 1<<19 - self.Open = Open - - def test_set_name(self): - class Descriptor(object): - name = None - def __get__(self, instance, owner_class=None): - if instance is None: - return self - else: - return instance.__dict__[self.name] - def __set__(self, instance, value): - instance.__dict__[self.name] = value - def __set_name__(self, owner, name): - self.name = name - # - class AnEnum(Enum): - ONE = 1 - two = Descriptor() - # - self.assertEqual(list(AnEnum), [AnEnum.ONE]) - self.assertEqual(AnEnum.two.name, 'two') - AnEnum.ONE.two = 'three' - self.assertEqual(AnEnum.ONE.two, 'three') - self.assertEqual(AnEnum.ONE.__dict__['two'], 'three') - - def test_new_with_keywords(self): - class Huh(IntFlag): - __order__ = 'PLAIN BOLD_ITALIC HIGHLIGHT' - def __new__(cls, docstring, open=None, close=None): - if cls.__members__: - value = 2 ** (len(cls.__members__)-1) - else: - value = 0 - member = int.__new__(cls, value) - if open and close is None: - close = open - member.open = open - member.close = close - member.__doc__ = docstring - member._value_ = value - return member - PLAIN = 'normal' - BOLD_ITALIC = '***really super important***', '***' - HIGHLIGHT = 'please ==take notice==', '==', '==' - p = Huh.PLAIN - self.assertTrue(type(p) is Huh, type(p)) - self.assertEqual( - (p.value, p.__doc__, p.open, p.close), - (0, 'normal', None, None), - ) - bi = Huh.BOLD_ITALIC - self.assertEqual( - (bi.value, bi.__doc__, bi.open, bi.close), - (1, '***really super important***', '***', '***'), - ) - h = Huh.HIGHLIGHT - self.assertEqual( - (h.value, h.__doc__, h.open, h.close), - (2, 'please ==take notice==', '==', '=='), - ) - - def test_private_names(self): - class Private(Enum): - __corporal = 'Radar' - __major_ = 'Hoolihan' - self.assertEqual(len(Private), 0) - self.assertEqual(Private._Private__corporal, 'Radar') - self.assertFalse(isinstance(Private._Private__corporal, Enum)) - self.assertEqual(Private._Private__major_, 'Hoolihan') - self.assertFalse(isinstance(Private._Private__major_, Enum)) - - def test_auto_alias(self): - Fun = self.Fun - self.assertEqual( - list(Fun), - [Fun.ONE, Fun.TWO, Fun.FOUR, Fun.EIGHT], - ) - self.assertEqual(Fun.THREE._value_, 3) - self.assertEqual(repr(Fun.SEVEN), '') - self.assertEqual(list(Fun.SEVEN), [Fun.ONE, Fun.TWO, Fun.FOUR]) - - def test_str_is_str_str(self): - red, white = self.TermColor.FG_Red, self.TermColor.BG_White - barber = red | white - self.assertEqual(barber, '\x1b[31;47m') - self.assertEqual(barber.value, red.value | white.value) - self.assertEqual(barber.code, ';'.join([red.code, white.code])) - self.assertEqual(repr(barber), '') - self.assertEqual(str(barber), '\x1b[31;47m') - - def test_membership(self): - Color = self.Color - Open = self.Open - self.assertRaises(TypeError, lambda: 'BLACK' in Color) - self.assertRaises(TypeError, lambda: 'RO' in Open) - self.assertTrue(Color.BLACK in Color) - self.assertTrue(Open.RO in Open) - self.assertFalse(Color.BLACK in Open) - self.assertFalse(Open.RO in Color) - self.assertRaises(TypeError, lambda: 0 in Color) - self.assertRaises(TypeError, lambda: 0 in Open) - - def test_member_contains(self): - Color = self.Color - self.assertRaises(TypeError, lambda: 'test' in Color.BLUE) - self.assertRaises(TypeError, lambda: 2 in Color.BLUE) - self.assertTrue(Color.BLUE in Color.BLUE) - self.assertTrue(Color.BLUE in Color['RED|GREEN|BLUE']) - - def test_member_length(self): - self.assertEqual(self.Color.__len__(self.Color.BLACK), 0) - self.assertEqual(self.Color.__len__(self.Color.GREEN), 1) - self.assertEqual(self.Color.__len__(self.Color.PURPLE), 2) - self.assertEqual(self.Color.__len__(self.Color.BLANCO), 3) - - def test_number_reset_and_order_cleanup(self): - class Confused(Flag): - _order_ = 'ONE TWO FOUR DOS EIGHT SIXTEEN' - ONE = auto() - TWO = auto() - FOUR = auto() - DOS = 2 - EIGHT = auto() - SIXTEEN = auto() - self.assertEqual( - list(Confused), - [Confused.ONE, Confused.TWO, Confused.FOUR, Confused.EIGHT, Confused.SIXTEEN]) - self.assertIs(Confused.TWO, Confused.DOS) - self.assertEqual(Confused.DOS._value_, 2) - self.assertEqual(Confused.EIGHT._value_, 8) - self.assertEqual(Confused.SIXTEEN._value_, 16) - - def test_str(self): - Perm = self.Perm - self.assertEqual(str(Perm.R), 'Perm.R') - self.assertEqual(str(Perm.W), 'Perm.W') - self.assertEqual(str(Perm.X), 'Perm.X') - self.assertEqual(str(Perm.R | Perm.W), 'Perm.R|W') - self.assertEqual(str(Perm.R | Perm.W | Perm.X), 'Perm.R|W|X') - self.assertEqual(str(Perm(0)), 'Perm(0)') - self.assertEqual(str(~Perm.R), 'Perm.W|X') - self.assertEqual(str(~Perm.W), 'Perm.R|X') - self.assertEqual(str(~Perm.X), 'Perm.R|W') - self.assertEqual(str(~(Perm.R | Perm.W)), 'Perm.X') - self.assertEqual(str(~(Perm.R | Perm.W | Perm.X)), 'Perm(0)') - self.assertEqual(str(Perm(-1)), 'Perm.R|W|X') - self.assertEqual(str(Perm(~0)), 'Perm.R|W|X') - - Open = self.Open - self.assertEqual(str(Open.RO), 'Open.RO') - self.assertEqual(str(Open.WO), 'Open.WO') - self.assertEqual(str(Open.AC), 'Open.AC') - self.assertEqual(str(Open.RO | Open.CE), 'Open.CE') - self.assertEqual(str(Open.WO | Open.CE), 'Open.WO|CE') - self.assertEqual(str(~Open.RO), 'Open.WO|RW|CE') - self.assertEqual(str(~Open.WO), 'Open.RW|CE') - self.assertEqual(str(~Open.AC), 'Open.CE') - self.assertEqual(str(~(Open.RO | Open.CE)), 'Open.AC') - self.assertEqual(str(~(Open.WO | Open.CE)), 'Open.RW') - - def test_repr(self): - Perm = self.Perm - self.assertEqual(repr(Perm.R), '') - self.assertEqual(repr(Perm.W), '') - self.assertEqual(repr(Perm.X), '') - self.assertEqual(repr(Perm.R | Perm.W), '') - self.assertEqual(repr(Perm.R | Perm.W | Perm.X), '') - self.assertEqual(repr(Perm(0)), '') - self.assertEqual(repr(~Perm.R), '') - self.assertEqual(repr(~Perm.W), '') - self.assertEqual(repr(~Perm.X), '') - self.assertEqual(repr(~(Perm.R | Perm.W)), '') - self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '') - self.assertEqual(repr(Perm(~0)), '') - - Open = self.Open - self.assertEqual(repr(Open.RO), '') - self.assertEqual(repr(Open.WO), '') - self.assertEqual(repr(Open.AC), '') - self.assertEqual(repr(Open.RO | Open.CE), '') - self.assertEqual(repr(Open.WO | Open.CE), '') - self.assertEqual(repr(~Open.RO), '') - self.assertEqual(repr(~Open.WO), '') - self.assertEqual(repr(~Open.AC), '') - self.assertEqual(repr(~(Open.RO | Open.CE)), '') - self.assertEqual(repr(~(Open.WO | Open.CE)), '') - - def test_name_lookup(self): - Color = self.Color - self.assertTrue(Color.RED is Color['RED']) - self.assertTrue(Color.RED|Color.GREEN is Color['RED|GREEN']) - self.assertTrue(Color.PURPLE is Color['RED|BLUE']) - - def test_or(self): - Perm = self.Perm - for i in Perm: - for j in Perm: - self.assertEqual((i | j), Perm(i.value | j.value)) - self.assertEqual((i | j).value, i.value | j.value) - self.assertIs(type(i | j), Perm) - for i in Perm: - self.assertIs(i | i, i) - Open = self.Open - self.assertIs(Open.RO | Open.CE, Open.CE) - - def test_and(self): - Perm = self.Perm - RW = Perm.R | Perm.W - RX = Perm.R | Perm.X - WX = Perm.W | Perm.X - RWX = Perm.R | Perm.W | Perm.X - values = list(Perm) + [RW, RX, WX, RWX, Perm(0)] - for i in values: - for j in values: - self.assertEqual((i & j).value, i.value & j.value) - self.assertIs(type(i & j), Perm) - for i in Perm: - self.assertIs(i & i, i) - self.assertIs(i & RWX, i) - self.assertIs(RWX & i, i) - Open = self.Open - self.assertIs(Open.RO & Open.CE, Open.RO) - - def test_xor(self): - Perm = self.Perm - for i in Perm: - for j in Perm: - self.assertEqual((i ^ j).value, i.value ^ j.value) - self.assertIs(type(i ^ j), Perm) - for i in Perm: - self.assertIs(i ^ Perm(0), i) - self.assertIs(Perm(0) ^ i, i) - Open = self.Open - self.assertIs(Open.RO ^ Open.CE, Open.CE) - self.assertIs(Open.CE ^ Open.CE, Open.RO) - - def test_invert(self): - Perm = self.Perm - RW = Perm.R | Perm.W - RX = Perm.R | Perm.X - WX = Perm.W | Perm.X - RWX = Perm.R | Perm.W | Perm.X - values = list(Perm) + [RW, RX, WX, RWX, Perm(0)] - for i in values: - self.assertIs(type(~i), Perm) - self.assertEqual(~~i, i) - for i in Perm: - self.assertIs(~~i, i) - Open = self.Open - self.assertIs(Open.WO & ~Open.WO, Open.RO) - self.assertIs((Open.WO|Open.CE) & ~Open.WO, Open.CE) - - def test_bool(self): - Perm = self.Perm - for f in Perm: - self.assertTrue(f) - Open = self.Open - for f in Open: - self.assertEqual(bool(f.value), bool(f)) - - def test_doc_flag(self): - class DocFlag(Flag): - _init_ = 'value __doc__' - _start_ = 0 - # def __new__(cls, value, doc=None): - # # if doc is None and isinstance(value, basestring): - # # value, doc = doc, value - # # if value is None: - # # if not len(cls): - # # value = 0 - # # else: - # # value = 2 ** (len(cls) -1) - # # if not isinstance(value, baseinteger): - # # raise TypeError("%r is not a valid %s value" % (value, cls.__name__)) - # obj = object.__new__(cls) - # # if doc is None, don't mess with the value - # if doc: - # value = value >> 1 - # obj._value_ = value - # obj.__doc__ = doc - # return obj - # - class AddressSegment(DocFlag): - _order_ = 'UNKNOWN PO PO_TYPE NUMBER PREORD NAME STREET POSTORD SECONDARY_TYPE SECONDARY_NUMBER AND' - UNKNOWN = "unable to determine address element type" - PO = "post office delivery" - PO_TYPE = "box or drawer" - NUMBER = "main unit designator" - PREORD = "N S E W etc" - NAME = "street name" - STREET = "st ave blvd etc" - POSTORD = "N S E W etc" - SECONDARY_TYPE = "apt bldg floor etc" - SECONDARY_NUMBER = "secondary unit designator" - AND = "& indicates a corner address" - AS = AddressSegment - self.assertEqual(AS.NAME._value_, 16) - self.assertEqual(AS.STREET._value_, 32) - self.assertEqual(AS.SECONDARY_TYPE._value_, 128) - self.assertEqual((AS.NAME | AS.STREET)._value_, 48, "%r is not 48" % (AS.NAME | AS.STREET)) - - def test_iteration(self): - C = self.Color - self.assertEqual(list(C), [C.RED, C.GREEN, C.BLUE]) - self.assertEqual(list(C.PURPLE), [C.RED, C.BLUE]) - - def test_member_iteration(self): - C = self.Color - self.assertEqual(list(C.BLACK), []) - self.assertEqual(list(C.RED), [C.RED]) - self.assertEqual(list(C.PURPLE), [C.RED, C.BLUE]) - - def test_programatic_function_string(self): - Perm = Flag('Perm', 'R W X') - lst = list(Perm) - self.assertEqual(len(lst), len(Perm)) - self.assertEqual(len(Perm), 3, Perm) - self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) - for i, n in enumerate('R W X'.split()): - v = 1<' % (self.__class__.__name__, self._name_) - self.assertTrue(isinstance(Color.FG_Black, Color)) - self.assertTrue(isinstance(Color.FG_Black, str)) - self.assertEqual(Color.FG_Black, '\x1b[30m') - self.assertEqual(Color.FG_Black.code, '30') - colors = Color.BG_Magenta | Color.FG_Black - self.assertTrue(isinstance(colors, Color)) - self.assertTrue(isinstance(colors, str)) - self.assertEqual(colors, '\x1b[30;45m') - self.assertEqual(colors.code, '30;45') - self.assertEqual(repr(colors), '') - - def test_sub_subclass_with_new_new(self): - class StrFlag(str, Flag): - def __new__(cls, value, code): - str_value = '\x1b[%sm' % code - obj = str.__new__(cls, str_value) - obj._value_ = value - obj.code = code - return obj - @classmethod - def _create_pseudo_member_(cls, value): - # calculate the code - members = list(cls._iter_member_(value)) - code = ';'.join(m.code for m in members) - pseudo_member = super(StrFlag, cls)._create_pseudo_member_(value, code) - return pseudo_member - # - class Color(StrFlag): - _order_ = 'FG_Black FG_Red FG_Green FG_Blue BG_Yellow BG_Magenta BG_Cyan BG_White' - def __new__(cls, value, string, abbr): - str_value = (abbr or '').title() - obj = str.__new__(cls, str_value) - obj._value_ = value - obj.code = string - obj.abbr = abbr - return obj - # # FOREGROUND - 30s BACKGROUND - 40s: - FG_Black = '30', 'blk' # ESC [ 30 m # black - FG_Red = '31', 'red' # ESC [ 31 m # red - FG_Green = '32', 'grn' # ESC [ 32 m # green - FG_Blue = '34', 'blu' # ESC [ 34 m # blue - # - BG_Yellow = '43', 'ylw' # ESC [ 33 m # yellow - BG_Magenta = '45', 'mag' # ESC [ 35 m # magenta - BG_Cyan = '46', 'cyn' # ESC [ 36 m # cyan - BG_White = '47', 'wht' # ESC [ 37 m # white - # - def __repr__(self): - if self._name_ is not None: - return '<%s.%s>' % (self.__class__.__name__, self._name_) - else: - return '<%s: %s>' % (self.__class__.__name__, '|'.join([m.name for m in self])) - self.assertTrue(isinstance(Color.FG_Black, Color)) - self.assertTrue(isinstance(Color.FG_Black, str)) - self.assertEqual(Color.FG_Black, 'Blk', str.__repr__(Color.FG_Black)) - self.assertEqual(Color.FG_Black.abbr, 'blk') - - def test_subclass_with_default_new(self): - class MyFlag(str, Flag): - _order_ = 'this these theother' - this = 'that' - these = 'those' - theother = 'thingimibobs' - self.assertEqual(MyFlag.this, 'that') - self.assertEqual(MyFlag.this.value, 1) - self.assertEqual(MyFlag.these, 'those') - self.assertEqual(MyFlag.these.value, 2) - self.assertEqual(MyFlag.theother, 'thingimibobs') - self.assertEqual(MyFlag.theother.value, 4) - - def test_subclass_a_bunch(self): - class Color(str, Flag): - _order_ = 'FG_Black FG_Red FG_Green FG_Blue BG_Yellow BG_Magenta BG_Cyan BG_White' - def __new__(cls, value, code): - str_value = '\x1b[%sm' % code - obj = str.__new__(cls, str_value) - obj._value_ = value - obj.code = code - return obj - @staticmethod - def _generate_next_value_(name, start, count, values, *args, **kwds): - return (2 ** count, ) + args - @classmethod - def _create_pseudo_member_(cls, value): - # calculate the code - members = list(cls._iter_member_(value)) - code = ';'.join(m.code for m in members) - pseudo_member = super(Color, cls)._create_pseudo_member_(value, code) - return pseudo_member - # - # # FOREGROUND - 30s BACKGROUND - 40s: - FG_Black = '30' # ESC [ 30 m # black - FG_Red = '31' # ESC [ 31 m # red - FG_Green = '32' # ESC [ 32 m # green - FG_Blue = '34' # ESC [ 34 m # blue - # - BG_Yellow = '43' # ESC [ 33 m # yellow - BG_Magenta = '45' # ESC [ 35 m # magenta - BG_Cyan = '46' # ESC [ 36 m # cyan - BG_White = '47' # ESC [ 37 m # white - # - def __repr__(self): - if self._name_ is not None: - return '<%s.%s>' % (self.__class__.__name__, self._name_) - else: - return '<%s: %s>' % (self.__class__.__name__, '|'.join([m.name for m in self])) - # - Purple = Color.BG_Magenta | Color.FG_Blue - self.assertTrue(isinstance(Purple, Color)) - self.assertTrue(isinstance(Purple, str)) - self.assertIs(Purple, Color.BG_Magenta | Color.FG_Blue) - self.assertEqual(Purple, '\x1b[34;45m') - self.assertEqual(Purple.code, '34;45') - self.assertEqual(Purple.name, 'FG_Blue|BG_Magenta') - - def test_init_subclass(self): - class MyEnum(Flag): - def __init_subclass__(cls, **kwds): - super(MyEnum, cls).__init_subclass__(**kwds) - self.assertFalse(cls.__dict__.get('_test', False)) - cls._test1 = 'MyEnum' - # - class TheirEnum(MyEnum): - def __init_subclass__(cls, **kwds): - super(TheirEnum, cls).__init_subclass__(**kwds) - cls._test2 = 'TheirEnum' - class WhoseEnum(TheirEnum): - def __init_subclass__(cls, **kwds): - pass - class NoEnum(WhoseEnum): - ONE = 1 - self.assertEqual(TheirEnum.__dict__['_test1'], 'MyEnum') - self.assertEqual(WhoseEnum.__dict__['_test1'], 'MyEnum') - self.assertEqual(WhoseEnum.__dict__['_test2'], 'TheirEnum') - self.assertFalse(NoEnum.__dict__.get('_test1', False)) - self.assertFalse(NoEnum.__dict__.get('_test2', False)) - # - class OurEnum(MyEnum): - def __init_subclass__(cls, **kwds): - cls._test2 = 'OurEnum' - class WhereEnum(OurEnum): - def __init_subclass__(cls, **kwds): - pass - class NeverEnum(WhereEnum): - ONE = 1 - self.assertEqual(OurEnum.__dict__['_test1'], 'MyEnum') - self.assertFalse(WhereEnum.__dict__.get('_test1', False)) - self.assertEqual(WhereEnum.__dict__['_test2'], 'OurEnum') - self.assertFalse(NeverEnum.__dict__.get('_test1', False)) - self.assertFalse(NeverEnum.__dict__.get('_test2', False)) - - def test_int_long_conversion(self): - class Perm(Flag): - EXEC = 1 << 0 - WRITE = 1 << 1 - READ = 1 << 2 - MSB32 = 1 << 31 - MSB64 = 1 << 63 - - # 32-bit system test - self.assertEqual(Perm.MSB32, Perm(0x80000000)) - self.assertEqual(Perm.WRITE|Perm.MSB32, Perm(0x80000002)) - - # 64-bit system test - self.assertEqual(Perm.MSB64, Perm(0x8000000000000000)) - self.assertEqual(Perm.MSB64|Perm.WRITE, Perm(0x8000000000000002)) - - -class TestIntFlag(TestCase): - """Tests of the IntFlags.""" - - def setUp(self): - # - class Perm(IntFlag): - _order_ = 'R W X' - R = 1 << 2 - W = 1 << 1 - X = 1 << 0 - # - class Color(IntFlag): - BLACK = 0 - RED = 1 - GREEN = 2 - BLUE = 4 - PURPLE = RED|BLUE - # - class Open(IntFlag): - "not a good flag candidate" - RO = 0 - WO = 1 - RW = 2 - AC = 3 - CE = 1<<19 - # - self.Perm = Perm - self.Color = Color - self.Open = Open - - def test_set_name(self): - class Descriptor(object): - name = None - def __get__(self, instance, owner_class=None): - if instance is None: - return self - else: - return instance.__dict__[self.name] - def __set__(self, instance, value): - instance.__dict__[self.name] = value - def __set_name__(self, owner, name): - self.name = name - # - class AnEnum(Enum): - ONE = 1 - two = Descriptor() - # - self.assertEqual(list(AnEnum), [AnEnum.ONE]) - self.assertEqual(AnEnum.two.name, 'two') - AnEnum.ONE.two = 'three' - self.assertEqual(AnEnum.ONE.two, 'three') - self.assertEqual(AnEnum.ONE.__dict__['two'], 'three') - - def test_private_names(self): - class Private(Enum): - __corporal = 'Radar' - __major_ = 'Hoolihan' - self.assertEqual(len(Private), 0) - self.assertEqual(Private._Private__corporal, 'Radar') - self.assertFalse(isinstance(Private._Private__corporal, Enum)) - self.assertEqual(Private._Private__major_, 'Hoolihan') - self.assertFalse(isinstance(Private._Private__major_, Enum)) - - def test_membership(self): - Color = self.Color - Open = self.Open - self.assertRaises(TypeError, lambda: 'GREEN' in Color) - self.assertRaises(TypeError, lambda: 'RW' in Open) - self.assertTrue(Color.GREEN in Color) - self.assertTrue(Open.RW in Open) - self.assertFalse(Color.GREEN in Open) - self.assertFalse(Open.RW in Color) - self.assertRaises(TypeError, lambda: 2 in Color) - self.assertRaises(TypeError, lambda: 2 in Open) - - def test_member_contains(self): - Color = self.Color - self.assertRaises(TypeError, lambda: 'test' in Color.RED) - self.assertRaises(TypeError, lambda: 1 in Color.RED) - self.assertTrue(Color.RED in Color.RED) - self.assertTrue(Color.RED in Color.PURPLE) - - def test_name_lookup(self): - Color = self.Color - self.assertTrue(Color.RED is Color['RED']) - self.assertTrue(Color.RED|Color.GREEN is Color['RED|GREEN']) - self.assertTrue(Color.PURPLE is Color['RED|BLUE']) - - def test_type(self): - Perm = self.Perm - Open = self.Open - for f in Perm: - self.assertTrue(isinstance(f, Perm)) - self.assertEqual(f, f.value) - self.assertTrue(isinstance(Perm.W | Perm.X, Perm)) - self.assertEqual(Perm.W | Perm.X, 3) - for f in Open: - self.assertTrue(isinstance(f, Open)) - self.assertEqual(f, f.value) - self.assertTrue(isinstance(Open.WO | Open.RW, Open)) - self.assertEqual(Open.WO | Open.RW, 3) - - - def test_str(self): - Perm = self.Perm - self.assertEqual(str(Perm.R), '4') - self.assertEqual(str(Perm.W), '2') - self.assertEqual(str(Perm.X), '1') - self.assertEqual(str(Perm.R | Perm.W), '6') - self.assertEqual(str(Perm.R | Perm.W | Perm.X), '7') - self.assertEqual(str(Perm(0)), '0') - self.assertEqual(str(~Perm.R), '3') - self.assertEqual(str(~Perm.W), '5') - self.assertEqual(str(~Perm.X), '6') - self.assertEqual(str(~(Perm.R | Perm.W)), '1') - self.assertEqual(str(~(Perm.R | Perm.W | Perm.X)), '0') - self.assertEqual(str(Perm(~0)), '7') - - Open = self.Open - self.assertEqual(str(Open.RO), '0') - self.assertEqual(str(Open.WO), '1') - self.assertEqual(str(Open.AC), '3') - self.assertEqual(str(Open.RO | Open.CE), '524288') - self.assertEqual(str(Open.WO | Open.CE), '524289') - self.assertEqual(str(~Open.RO), '524291') - self.assertEqual(str(~Open.WO), '524290') - self.assertEqual(str(~Open.AC), '524288') - self.assertEqual(str(~(Open.RO | Open.CE)), '3') - self.assertEqual(str(~(Open.WO | Open.CE)), '2') - - def test_repr_strict(self): - class Perm(IntFlag): - _order_ = 'R W X' - R = 1 << 2 - W = 1 << 1 - X = 1 << 0 - Perm._boundary_ = aenum.STRICT - self.assertEqual(repr(Perm.R), '') - self.assertEqual(repr(Perm.W), '') - self.assertEqual(repr(Perm.X), '') - self.assertEqual(repr(Perm.R | Perm.W), '') - self.assertEqual(repr(Perm.R | Perm.W | Perm.X), '') - self.assertEqual(repr(Perm(0)), '') - self.assertEqual(repr(~Perm.R), '') - self.assertEqual(repr(~Perm.W), '') - self.assertEqual(repr(~Perm.X), '') - self.assertEqual(repr(~(Perm.R | Perm.W)), '') - self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '') - # - with self.assertRaisesRegex(ValueError, r'invalid value: 12'): - repr(Perm.R | 8) - with self.assertRaisesRegex(ValueError, r'invalid value: 12'): - repr(~(Perm.R | 8)) - with self.assertRaisesRegex(ValueError, r'invalid value: -9'): - repr(Perm(~8)) - - def test_repr_conform(self): - class Perm(IntFlag): - _order_ = 'R W X' - R = 1 << 2 - W = 1 << 1 - X = 1 << 0 - Perm._boundary_ = aenum.CONFORM - self.assertEqual(repr(Perm.R), '') - self.assertEqual(repr(Perm.W), '') - self.assertEqual(repr(Perm.X), '') - self.assertEqual(repr(Perm.R | Perm.W), '') - self.assertEqual(repr(Perm.R | Perm.W | Perm.X), '') - self.assertEqual(repr(Perm(0)), '') - self.assertEqual(repr(~Perm.R), '') - self.assertEqual(repr(~Perm.W), '') - self.assertEqual(repr(~Perm.X), '') - self.assertEqual(repr(~(Perm.R | Perm.W)), '') - self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '') - self.assertEqual(repr(Perm.R | 8), '') - self.assertEqual(repr(Perm(8)), '') - self.assertEqual(repr(~(Perm.R | 8)), '') - self.assertEqual(repr(Perm(~8)), '') - - def test_repr_eject(self): - class Perm(IntFlag): - _order_ = 'R W X' - _boundary_ = EJECT - R = 1 << 2 - W = 1 << 1 - X = 1 << 0 - self.assertEqual(repr(Perm.R), '') - self.assertEqual(repr(Perm.W), '') - self.assertEqual(repr(Perm.X), '') - self.assertEqual(repr(Perm.R | Perm.W), '') - self.assertEqual(repr(Perm.R | Perm.W | Perm.X), '') - self.assertEqual(repr(Perm(0)), '') - self.assertEqual(repr(~Perm.R), '') - self.assertEqual(repr(~Perm.W), '') - self.assertEqual(repr(~Perm.X), '') - self.assertEqual(repr(~(Perm.R | Perm.W)), '') - self.assertEqual(repr(~(Perm.R | Perm.W | Perm.X)), '') - self.assertEqual(repr(Perm.R | 8), '12') - self.assertEqual(repr(Perm(8)), '8') - self.assertEqual(repr(~(Perm.R | 8)), '-13') - self.assertEqual(repr(Perm(~8)), '-9') - - def test_repr_open(self): - class Open(IntFlag): - "not a good flag candidate" - RO = 0 - WO = 1 - RW = 2 - AC = 3 - CE = 1<<19 - Open._boundary_ = aenum.STRICT - self.assertEqual(repr(Open.RO), '') - self.assertEqual(repr(Open.WO), '') - self.assertEqual(repr(Open.AC), '') - self.assertEqual(repr(Open.RO | Open.CE), '') - self.assertEqual(repr(Open.WO | Open.CE), '') - self.assertEqual(repr(~Open.RO), '') - self.assertEqual(repr(~Open.WO), '') - self.assertEqual(repr(~Open.AC), '') - self.assertEqual(repr(~(Open.RO | Open.CE)), '') - self.assertEqual(repr(~(Open.WO | Open.CE)), '') - with self.assertRaisesRegex(ValueError, r'invalid value: -5'): - repr(Open(~4)) - with self.assertRaisesRegex(ValueError, r'invalid value: 4'): - repr(Open(4)) - # - class Open(IntFlag): - "not a good flag candidate" - RO = 0 - WO = 1 - RW = 2 - AC = 3 - CE = 1<<19 - Open._boundary_ = aenum.CONFORM - self.assertEqual(repr(Open.RO), '') - self.assertEqual(repr(Open.WO), '') - self.assertEqual(repr(Open.AC), '') - self.assertEqual(repr(Open.RO | Open.CE), '') - self.assertEqual(repr(Open.WO | Open.CE), '') - self.assertEqual(repr(~Open.RO), '') - self.assertEqual(repr(~Open.WO), '') - self.assertEqual(repr(~Open.AC), '') - self.assertEqual(repr(~(Open.RO | Open.CE)), '') - self.assertEqual(repr(~(Open.WO | Open.CE)), '') - self.assertEqual(repr(Open(~4)), '') - self.assertEqual(repr(Open(4)), '') - # - class Open(IntFlag): - "not a good flag candidate" - RO = 0 - WO = 1 - RW = 2 - AC = 3 - CE = 1<<19 - Open._boundary_ = aenum.EJECT - self.assertEqual(repr(Open.RO), '') - self.assertEqual(repr(Open.WO), '') - self.assertEqual(repr(Open.AC), '') - self.assertEqual(repr(Open.RO | Open.CE), '') - self.assertEqual(repr(Open.WO | Open.CE), '') - self.assertEqual(repr(~Open.RO), '') - self.assertEqual(repr(~Open.WO), '') - self.assertEqual(repr(~Open.AC), '') - self.assertEqual(repr(~(Open.RO | Open.CE)), '') - self.assertEqual(repr(~(Open.WO | Open.CE)), '') - self.assertEqual(repr(Open(~4)), '-5') - self.assertEqual(repr(Open(4)), '4') - - def test_or(self): - Perm = self.Perm - for i in Perm: - for j in Perm: - self.assertEqual(i | j, i.value | j.value) - self.assertEqual((i | j).value, i.value | j.value) - self.assertIs(type(i | j), Perm) - for j in range(8): - self.assertEqual(i | j, i.value | j) - self.assertEqual((i | j).value, i.value | j) - self.assertIs(type(i | j), Perm) - self.assertEqual(j | i, j | i.value) - self.assertEqual((j | i).value, j | i.value) - self.assertIs(type(j | i), Perm) - for i in Perm: - self.assertIs(i | i, i) - self.assertIs(i | 0, i) - self.assertIs(0 | i, i) - Open = self.Open - self.assertIs(Open.RO | Open.CE, Open.CE) - - def test_and(self): - Perm = self.Perm - RW = Perm.R | Perm.W - RX = Perm.R | Perm.X - WX = Perm.W | Perm.X - RWX = Perm.R | Perm.W | Perm.X - values = list(Perm) + [RW, RX, WX, RWX, Perm(0)] - for i in values: - for j in values: - self.assertEqual(i & j, i.value & j.value, 'i is %r, j is %r' % (i, j)) - self.assertEqual((i & j).value, i.value & j.value, 'i is %r, j is %r' % (i, j)) - self.assertIs(type(i & j), Perm, 'i is %r, j is %r' % (i, j)) - for j in range(8): - self.assertEqual(i & j, i.value & j) - self.assertEqual((i & j).value, i.value & j) - self.assertIs(type(i & j), Perm) - self.assertEqual(j & i, j & i.value) - self.assertEqual((j & i).value, j & i.value) - self.assertIs(type(j & i), Perm) - for i in Perm: - self.assertIs(i & i, i) - self.assertIs(i & 7, i) - self.assertIs(7 & i, i) - Open = self.Open - self.assertIs(Open.RO & Open.CE, Open.RO) - - def test_xor(self): - Perm = self.Perm - for i in Perm: - for j in Perm: - self.assertEqual(i ^ j, i.value ^ j.value) - self.assertEqual((i ^ j).value, i.value ^ j.value) - self.assertIs(type(i ^ j), Perm) - for j in range(8): - self.assertEqual(i ^ j, i.value ^ j) - self.assertEqual((i ^ j).value, i.value ^ j) - self.assertIs(type(i ^ j), Perm) - self.assertEqual(j ^ i, j ^ i.value) - self.assertEqual((j ^ i).value, j ^ i.value) - self.assertIs(type(j ^ i), Perm) - for i in Perm: - self.assertIs(i ^ 0, i) - self.assertIs(0 ^ i, i) - Open = self.Open - self.assertIs(Open.RO ^ Open.CE, Open.CE) - self.assertIs(Open.CE ^ Open.CE, Open.RO) - - def test_invert(self): - Perm = self.Perm - RW = Perm.R | Perm.W - RX = Perm.R | Perm.X - WX = Perm.W | Perm.X - RWX = Perm.R | Perm.W | Perm.X - values = list(Perm) + [RW, RX, WX, RWX, Perm(0)] - for i in values: - self.assertEqual(~i, (~i).value) - self.assertIs(type(~i), Perm) - self.assertEqual(~~i, i) - for i in Perm: - self.assertIs(~~i, i) - Open = self.Open - self.assertIs(Open.WO & ~Open.WO, Open.RO) - self.assertIs((Open.WO|Open.CE) & ~Open.WO, Open.CE) - - def test_iter(self): - Perm = self.Perm - NoPerm = Perm.R ^ Perm.R - RWX = Perm.R | Perm.W | Perm.X - self.assertEqual(list(NoPerm), []) - self.assertEqual(list(Perm.R), [Perm.R]) - self.assertEqual(list(RWX), [Perm.R, Perm.W, Perm.X]) - - def test_programatic_function_string(self): - Perm = IntFlag('Perm', 'R W X') - lst = list(Perm) - self.assertEqual(len(lst), len(Perm)) - self.assertEqual(len(Perm), 3, Perm) - self.assertEqual(lst, [Perm.R, Perm.W, Perm.X]) - for i, n in enumerate('R W X'.split()): - v = 1< one' in message) - - try: - class Dirtier(IntEnum): - __order__ = 'single triple' - single = 1 - double = 1 - triple = 3 - turkey = 3 - unique(Dirtier) - except ValueError: - exc = sys.exc_info()[1] - message = exc.args[0] - self.assertTrue('double -> single' in message) - self.assertTrue('turkey -> triple' in message) - - def test_unique_with_name(self): - @unique - class Silly(Enum): - one = 1 - two = 'dos' - name = 3 - @unique - class Sillier(IntEnum): - single = 1 - name = 2 - triple = 3 - value = 4 - - -class TestNamedTuple(TestCase): - - def test_explicit_indexing(self): - class Person(NamedTuple): - age = 0 - first = 1 - last = 2 - p1 = Person(17, 'John', 'Doe') - p2 = Person(21, 'Jane', 'Doe') - self.assertEqual(p1[0], 17) - self.assertEqual(p1[1], 'John') - self.assertEqual(p1[2], 'Doe') - self.assertEqual(p2[0], 21) - self.assertEqual(p2[1], 'Jane') - self.assertEqual(p2[2], 'Doe') - self.assertEqual(p1.age, 17) - self.assertEqual(p1.first, 'John') - self.assertEqual(p1.last, 'Doe') - self.assertEqual(p2.age, 21) - self.assertEqual(p2.first, 'Jane') - self.assertEqual(p2.last, 'Doe') - - def test_implicit_indexing(self): - class Person(NamedTuple): - __order__ = "age first last" - age = "person's age" - first = "person's first name" - last = "person's last name" - p1 = Person(17, 'John', 'Doe') - p2 = Person(21, 'Jane', 'Doe') - self.assertEqual(p1[0], 17) - self.assertEqual(p1[1], 'John') - self.assertEqual(p1[2], 'Doe') - self.assertEqual(p2[0], 21) - self.assertEqual(p2[1], 'Jane') - self.assertEqual(p2[2], 'Doe') - self.assertEqual(p1.age, 17) - self.assertEqual(p1.first, 'John') - self.assertEqual(p1.last, 'Doe') - self.assertEqual(p2.age, 21) - self.assertEqual(p2.first, 'Jane') - self.assertEqual(p2.last, 'Doe') - - def test_mixed_indexing(self): - class Person(NamedTuple): - __order__ = "age last cars" - age = "person's age" - last = 2, "person's last name" - cars = "person's cars" - p1 = Person(17, 'John', 'Doe', 3) - p2 = Person(21, 'Jane', 'Doe', 9) - self.assertEqual(p1[0], 17) - self.assertEqual(p1[1], 'John') - self.assertEqual(p1[2], 'Doe') - self.assertEqual(p1[3], 3) - self.assertEqual(p2[0], 21) - self.assertEqual(p2[1], 'Jane') - self.assertEqual(p2[2], 'Doe') - self.assertEqual(p2[3], 9) - self.assertEqual(p1.age, 17) - self.assertEqual(p1.last, 'Doe') - self.assertEqual(p1.cars, 3) - self.assertEqual(p2.age, 21) - self.assertEqual(p2.last, 'Doe') - self.assertEqual(p2.cars, 9) - - def test_issubclass(self): - class Person(NamedTuple): - age = 0 - first = 1 - last = 2 - self.assertTrue(issubclass(Person, NamedTuple)) - self.assertTrue(issubclass(Person, tuple)) - - def test_isinstance(self): - class Person(NamedTuple): - age = 0 - first = 1 - last = 2 - p1 = Person(17, 'John', 'Doe') - self.assertTrue(isinstance(p1, Person)) - self.assertTrue(isinstance(p1, NamedTuple)) - self.assertTrue(isinstance(p1, tuple)) - - def test_explicit_indexing_after_functional_api(self): - Person = NamedTuple('Person', (('age', 0), ('first', 1), ('last', 2))) - p1 = Person(17, 'John', 'Doe') - p2 = Person(21, 'Jane', 'Doe') - self.assertEqual(p1[0], 17) - self.assertEqual(p1[1], 'John') - self.assertEqual(p1[2], 'Doe') - self.assertEqual(p2[0], 21) - self.assertEqual(p2[1], 'Jane') - self.assertEqual(p2[2], 'Doe') - self.assertEqual(p1.age, 17) - self.assertEqual(p1.first, 'John') - self.assertEqual(p1.last, 'Doe') - self.assertEqual(p2.age, 21) - self.assertEqual(p2.first, 'Jane') - self.assertEqual(p2.last, 'Doe') - - def test_implicit_indexing_after_functional_api(self): - Person = NamedTuple('Person', 'age first last') - p1 = Person(17, 'John', 'Doe') - p2 = Person(21, 'Jane', 'Doe') - self.assertEqual(p1[0], 17) - self.assertEqual(p1[1], 'John') - self.assertEqual(p1[2], 'Doe') - self.assertEqual(p2[0], 21) - self.assertEqual(p2[1], 'Jane') - self.assertEqual(p2[2], 'Doe') - self.assertEqual(p1.age, 17) - self.assertEqual(p1.first, 'John') - self.assertEqual(p1.last, 'Doe') - self.assertEqual(p2.age, 21) - self.assertEqual(p2.first, 'Jane') - self.assertEqual(p2.last, 'Doe') - - def test_mixed_indexing_after_functional_api(self): - Person = NamedTuple('Person', (('age', 0), ('last', 2), ('cars', 3))) - p1 = Person(17, 'John', 'Doe', 3) - p2 = Person(21, 'Jane', 'Doe', 9) - self.assertEqual(p1[0], 17) - self.assertEqual(p1[1], 'John') - self.assertEqual(p1[2], 'Doe') - self.assertEqual(p1[3], 3) - self.assertEqual(p2[0], 21) - self.assertEqual(p2[1], 'Jane') - self.assertEqual(p2[2], 'Doe') - self.assertEqual(p2[3], 9) - self.assertEqual(p1.age, 17) - self.assertEqual(p1.last, 'Doe') - self.assertEqual(p1.cars, 3) - self.assertEqual(p2.age, 21) - self.assertEqual(p2.last, 'Doe') - self.assertEqual(p2.cars, 9) - - def test_issubclass_after_functional_api(self): - Person = NamedTuple('Person', 'age first last') - self.assertTrue(issubclass(Person, NamedTuple)) - self.assertTrue(issubclass(Person, tuple)) - - def test_isinstance_after_functional_api(self): - Person = NamedTuple('Person', 'age first last') - p1 = Person(17, 'John', 'Doe') - self.assertTrue(isinstance(p1, Person)) - self.assertTrue(isinstance(p1, NamedTuple)) - self.assertTrue(isinstance(p1, tuple)) - - def test_creation_with_all_keywords(self): - Person = NamedTuple('Person', 'age first last') - p1 = Person(age=17, first='John', last='Doe') - self.assertEqual(p1[0], 17) - self.assertEqual(p1[1], 'John') - self.assertEqual(p1[2], 'Doe') - self.assertEqual(p1.age, 17) - self.assertEqual(p1.first, 'John') - self.assertEqual(p1.last, 'Doe') - - def test_creation_with_some_keywords(self): - Person = NamedTuple('Person', 'age first last') - p1 = Person(17, first='John', last='Doe') - self.assertEqual(p1[0], 17) - self.assertEqual(p1[1], 'John') - self.assertEqual(p1[2], 'Doe') - self.assertEqual(p1.age, 17) - self.assertEqual(p1.first, 'John') - self.assertEqual(p1.last, 'Doe') - p1 = Person(17, last='Doe', first='John') - self.assertEqual(p1[0], 17) - self.assertEqual(p1[1], 'John') - self.assertEqual(p1[2], 'Doe') - self.assertEqual(p1.age, 17) - self.assertEqual(p1.first, 'John') - self.assertEqual(p1.last, 'Doe') - - def test_custom_new(self): - class Book(NamedTuple): - title = 0 - author = 1 - genre = 2 - def __new__(cls, string): - args = [s.strip() for s in string.split(';')] - return super(Book, cls).__new__(cls, *tuple(args)) - b1 = Book('The Last Mohican; John Doe; Historical') - self.assertEqual(b1.title, 'The Last Mohican') - self.assertEqual(b1.author, 'John Doe') - self.assertEqual(b1.genre, 'Historical') - - def test_defaults_in_class(self): - class Character(NamedTuple): - name = 0 - gender = 1, None, 'male' - klass = 2, None, 'fighter' - for char in ( - {'name':'John Doe'}, - {'name':'William Pickney', 'klass':'scholar'}, - {'name':'Sarah Doughtery', 'gender':'female'}, - {'name':'Sissy Moonbeam', 'gender':'female', 'klass':'sorceress'}, - ): - c = Character(**char) - for name, value in (('name', None), ('gender','male'), ('klass','fighter')): - if name in char: - value = char[name] - self.assertEqual(getattr(c, name), value) - - def test_defaults_in_class_that_are_falsey(self): - class Point(NamedTuple): - x = 0, 'horizondal coordinate', 0 - y = 1, 'vertical coordinate', 0 - p = Point() - self.assertEqual(p.x, 0) - self.assertEqual(p.y, 0) - - def test_pickle_namedtuple_with_module(self): - if isinstance(LifeForm, Exception): - raise LifeForm - lf = LifeForm('this', 'that', 'theother') - test_pickle_dump_load(self.assertEqual, lf) - - def test_pickle_namedtuple_without_module(self): - if isinstance(DeathForm, Exception): - raise DeathForm - df = DeathForm('sickly green', '2x4', 'foul') - test_pickle_dump_load(self.assertEqual, df) - - def test_subclassing(self): - if isinstance(ThatsIt, Exception): - raise ThatsIt - ti = ThatsIt('Henry', 'Weinhardt') - self.assertEqual(ti.blah, 'Henry') - self.assertTrue(ti.what(), 'Henry') - test_pickle_dump_load(self.assertEqual, ti) - - def test_contains(self): - Book = NamedTuple('Book', 'title author genre') - b = Book('Teckla', 'Steven Brust', 'fantasy') - self.assertTrue('Teckla' in b) - self.assertTrue('Steven Brust' in b) - self.assertTrue('fantasy' in b) - - def test_fixed_size(self): - class Book(NamedTuple): - _size_ = TupleSize.fixed - title = 0 - author = 1 - genre = 2 - b = Book('Teckla', 'Steven Brust', 'fantasy') - self.assertTrue('Teckla' in b) - self.assertTrue('Steven Brust' in b) - self.assertTrue('fantasy' in b) - self.assertEqual(b.title, 'Teckla') - self.assertEqual(b.author, 'Steven Brust') - self.assertRaises(TypeError, Book, 'Teckla', 'Steven Brust') - self.assertRaises(TypeError, Book, 'Teckla') - - def test_minimum_size(self): - class Book(NamedTuple): - _size_ = TupleSize.minimum - title = 0 - author = 1 - b = Book('Teckla', 'Steven Brust', 'fantasy') - self.assertTrue('Teckla' in b) - self.assertTrue('Steven Brust' in b) - self.assertTrue('fantasy' in b) - self.assertEqual(b.title, 'Teckla') - self.assertEqual(b.author, 'Steven Brust') - b = Book('Teckla', 'Steven Brust') - self.assertTrue('Teckla' in b) - self.assertTrue('Steven Brust' in b) - self.assertEqual(b.title, 'Teckla') - self.assertEqual(b.author, 'Steven Brust') - self.assertRaises(TypeError, Book, 'Teckla') - - def test_variable_size(self): - class Book(NamedTuple): - _size_ = TupleSize.variable - title = 0 - author = 1 - genre = 2 - b = Book('Teckla', 'Steven Brust', 'fantasy') - self.assertTrue('Teckla' in b) - self.assertTrue('Steven Brust' in b) - self.assertTrue('fantasy' in b) - self.assertEqual(b.title, 'Teckla') - self.assertEqual(b.author, 'Steven Brust') - self.assertEqual(b.genre, 'fantasy') - b = Book('Teckla', 'Steven Brust') - self.assertTrue('Teckla' in b) - self.assertTrue('Steven Brust' in b) - self.assertEqual(b.title, 'Teckla') - self.assertEqual(b.author, 'Steven Brust') - self.assertRaises(AttributeError, getattr, b, 'genre') - self.assertRaises(TypeError, Book, title='Teckla', genre='fantasy') - self.assertRaises(TypeError, Book, author='Steven Brust') - - def test_combining_namedtuples(self): - class Point(NamedTuple): - x = 0, 'horizontal coordinate', 1 - y = 1, 'vertical coordinate', -1 - class Color(NamedTuple): - r = 0, 'red component', 11 - g = 1, 'green component', 29 - b = 2, 'blue component', 37 - Pixel1 = NamedTuple('Pixel', Point+Color, module=__name__) - class Pixel2(Point, Color): - "a colored dot" - class Pixel3(Point): - r = 2, 'red component', 11 - g = 3, 'green component', 29 - b = 4, 'blue component', 37 - self.assertEqual(Pixel1._fields_, 'x y r g b'.split()) - self.assertEqual(Pixel1.x.__doc__, 'horizontal coordinate') - self.assertEqual(Pixel1.x.default, 1) - self.assertEqual(Pixel1.y.__doc__, 'vertical coordinate') - self.assertEqual(Pixel1.y.default, -1) - self.assertEqual(Pixel1.r.__doc__, 'red component') - self.assertEqual(Pixel1.r.default, 11) - self.assertEqual(Pixel1.g.__doc__, 'green component') - self.assertEqual(Pixel1.g.default, 29) - self.assertEqual(Pixel1.b.__doc__, 'blue component') - self.assertEqual(Pixel1.b.default, 37) - self.assertEqual(Pixel2._fields_, 'x y r g b'.split()) - self.assertEqual(Pixel2.x.__doc__, 'horizontal coordinate') - self.assertEqual(Pixel2.x.default, 1) - self.assertEqual(Pixel2.y.__doc__, 'vertical coordinate') - self.assertEqual(Pixel2.y.default, -1) - self.assertEqual(Pixel2.r.__doc__, 'red component') - self.assertEqual(Pixel2.r.default, 11) - self.assertEqual(Pixel2.g.__doc__, 'green component') - self.assertEqual(Pixel2.g.default, 29) - self.assertEqual(Pixel2.b.__doc__, 'blue component') - self.assertEqual(Pixel2.b.default, 37) - self.assertEqual(Pixel3._fields_, 'x y r g b'.split()) - self.assertEqual(Pixel3.x.__doc__, 'horizontal coordinate') - self.assertEqual(Pixel3.x.default, 1) - self.assertEqual(Pixel3.y.__doc__, 'vertical coordinate') - self.assertEqual(Pixel3.y.default, -1) - self.assertEqual(Pixel3.r.__doc__, 'red component') - self.assertEqual(Pixel3.r.default, 11) - self.assertEqual(Pixel3.g.__doc__, 'green component') - self.assertEqual(Pixel3.g.default, 29) - self.assertEqual(Pixel3.b.__doc__, 'blue component') - self.assertEqual(Pixel3.b.default, 37) - - def test_function_api_type(self): - class Tester(NamedTuple): - def howdy(self): - return 'backwards', list(reversed(self)) - Testee = NamedTuple('Testee', 'a c e', type=Tester) - t = Testee(1, 2, 3) - self.assertEqual(t.howdy(), ('backwards', [3, 2, 1])) - - def test_asdict(self): - class Point(NamedTuple): - x = 0, 'horizontal coordinate', 1 - y = 1, 'vertical coordinate', -1 - class Color(NamedTuple): - r = 0, 'red component', 11 - g = 1, 'green component', 29 - b = 2, 'blue component', 37 - Pixel = NamedTuple('Pixel', Point+Color, module=__name__) - pixel = Pixel(99, -101, 255, 128, 0) - self.assertEqual(pixel._asdict(), {'x':99, 'y':-101, 'r':255, 'g':128, 'b':0}) - - def test_make(self): - class Point(NamedTuple): - x = 0, 'horizontal coordinate', 1 - y = 1, 'vertical coordinate', -1 - self.assertEqual(Point(4, 5), (4, 5)) - self.assertEqual(Point._make((4, 5)), (4, 5)) - - def test_replace(self): - class Color(NamedTuple): - r = 0, 'red component', 11 - g = 1, 'green component', 29 - b = 2, 'blue component', 37 - purple = Color(127, 0, 127) - mid_gray = purple._replace(g=127) - self.assertEqual(mid_gray, (127, 127, 127)) - - -class TestNamedConstant(TestCase): - - def test_constantness(self): - class K(NamedConstant): - PI = 3.141596 - TAU = 2 * PI - self.assertEqual(K.PI, 3.141596) - self.assertEqual(K.TAU, 2 * K.PI) - with self.assertRaisesRegex(AttributeError, r'cannot rebind constant'): - K.PI = 9 - with self.assertRaisesRegex(AttributeError, r'cannot delete constant'): - del K.PI - with self.assertRaisesRegex(AttributeError, r'cannot rebind constant'): - K('PI', 3) - self.assertTrue(K.PI in K) - self.assertTrue(K.TAU in K) - - def test_duplicates(self): - class CardNumber(NamedConstant): - ACE = 11 - TWO = 2 - THREE = 3 - FOUR = 4 - FIVE = 5 - SIX = 6 - SEVEN = 7 - EIGHT = 8 - NINE = 9 - TEN = 10 - JACK = 10 - QUEEN = 10 - KING = 10 - self.assertFalse(CardNumber.TEN is CardNumber.JACK) - self.assertEqual(CardNumber.TEN, CardNumber.JACK) - self.assertEqual(CardNumber.TEN, 10) - - def test_extend_constants(self): - class CardSuit(NamedConstant): - HEARTS = 1 - SPADES = 2 - DIAMONTS = 3 - CLUBS = 4 - self.assertEqual(CardSuit.HEARTS, 1) - stars = CardSuit('STARS', 5) - self.assertIs(stars, CardSuit.STARS) - self.assertEqual(CardSuit.STARS, 5) - self.assertTrue(CardSuit.STARS in CardSuit) - - def test_constant_with_docstring(self): - class Stuff(NamedConstant): - Artifact = constant(7, "lucky number!") - Bowling = 11 - HillWomp = constant(29, 'blah blah') - self.assertEqual(Stuff.Artifact, 7) - self.assertEqual(Stuff.Artifact.__doc__, 'lucky number!') - self.assertEqual(Stuff.Bowling, 11) - self.assertEqual(Stuff.Bowling.__doc__, None) - self.assertEqual(Stuff.HillWomp, 29) - self.assertEqual(Stuff.HillWomp.__doc__, 'blah blah') - - def test_deep_copy(self): - import copy - class APITypes(aenum.Constant): - STRING = "string" - INT = "int" - APITypes('string') - d = {"first": APITypes.STRING} - copy.deepcopy(d) - self.assertTrue(d['first'] is APITypes.STRING) - - def test_subclass_w_same_value(self): - class Foo(aenum.Constant): - BLA = 'bla1' - ABA = 'aba1' - class Bar(aenum.Constant): - BLA = Foo.BLA - ABA = 'aba2' - self.assertEqual(Foo.BLA, Bar.BLA) - self.assertFalse(Foo.BLA is Bar.BLA) - - -class TestStarImport(TestCase): - - def test_all_exports_names(self): - scope = {} - exec('from aenum import *', scope, scope) - self.assertIn('Enum', scope) - -class TestStackoverflowAnswers(TestCase): - - def test_self_referential_directions(self): - # https://stackoverflow.com/a/64000706/208880 - class Directions(Enum): - _order_ = 'NORTH WEST SOUTH EAST' - # - NORTH = 1, 0 - WEST = 0, 1 - SOUTH = -1, 0 - EAST = 0, -1 - # - def __init__(self, x, y): - self.x = x - self.y = y - if len(self.__class__): - # make links - all = list(self.__class__) - left, right = all[0], all[-1] - self.left = left - self.right = right - left.right = self - right.left = self - # - D = Directions - self.assertEqual(D.NORTH.value, (1, 0)) - self.assertTrue(D.NORTH.left is D.WEST) - self.assertTrue(D.SOUTH.right is D.WEST) - - def test_self_referential_rock_paper_scissors(self): - # https://stackoverflow.com/a/57085357/208880 - class RPS(Enum): - _order_ = 'Rock, Paper, Scissors' - # - Rock = "rock" - Paper = "paper" - Scissors = "scissors" - # - def __init__(self, value): - if len(self.__class__): - # make links - all = list(self.__class__) - first, previous = all[0], all[-1] - first.beats = self - self.beats = previous - # - self.assertTrue(RPS.Rock.beats is RPS.Scissors) - self.assertTrue(RPS.Scissors.beats is RPS.Paper) - self.assertTrue(RPS.Paper.beats is RPS.Rock) - - def test_arduino_headers(self): - # https://stackoverflow.com/q/65048495/208880 - class CHeader(Enum): - def __init_subclass__(cls, **kwds): - # write Enums to C header file - cls_name = cls.__name__ - header_path = getattr(cls, '_%s__header' % cls_name) - with open(header_path, 'w') as fh: - fh.write('initial header stuff here\n') - for enum in cls: - fh.write('#define %s %r\n' % (enum.name, enum.value)) - class Arduino(CHeader): - _order_ = 'ONE TWO' - __header = os.path.join(tempdir, 'arduino.h') - ONE = 1 - TWO = 2 - with open(os.path.join(tempdir, 'arduino.h')) as fh: - data = fh.read() - self.assertEqual(textwrap.dedent("""\ - initial header stuff here - #define ONE 1 - #define TWO 2 - """), - data, - ) - - def test_lowercase_compare(self): - # https://stackoverflow.com/q/65139026/208880 - class CompareLowerCase(Enum): - def __init_subclass__(cls, **kwds): - super(CompareLowerCase, cls).__init_subclass__(**kwds) - cls.lowered_names = set([m.name.lower() for m in cls]) - @classmethod - def has_name(cls, name): - return name.lower() in cls.lowered_names - # - class LabelEnum(CompareLowerCase, StrEnum): - ENUM_ONE = "Enum One" - ENUM_TWO = "Enum Two" - ENUM_THREE = "Enum Three" - FOUR = "FOUR" - FIVE = "FIVE" - SIX = "SIX" - # - self.assertTrue(LabelEnum.has_name('Enum_Three')) - - -class TestExtendEnum(TestCase): - - def test_extend_enum_plain(self): - class Color(Enum): - red = 1 - green = 2 - blue = 3 - self.assertRaisesRegex(TypeError, 'already in use as', extend_enum, Color, 'blue', 5) - # - extend_enum(Color, 'brown', 4) - self.assertEqual(Color.brown.name, 'brown') - self.assertEqual(Color.brown.value, 4) - self.assertTrue(Color.brown in Color) - self.assertEqual(Color(4), Color.brown) - self.assertEqual(Color['brown'], Color.brown) - self.assertEqual(len(Color), 4) - # - extend_enum(Color, 'mauve') - self.assertEqual(Color.mauve.name, 'mauve') - self.assertEqual(Color.mauve.value, 5) - self.assertTrue(Color.mauve in Color) - self.assertEqual(Color(5), Color.mauve) - self.assertEqual(Color['mauve'], Color.mauve) - self.assertEqual(len(Color), 5) - - def test_extend_enum_alias(self): - class Color(Enum): - red = 1 - green = 2 - blue = 3 - extend_enum(Color, 'rojo', 1) - self.assertEqual(Color.rojo.name, 'red') - self.assertEqual(Color.rojo.value, 1) - self.assertTrue(Color.rojo in Color) - self.assertEqual(Color(1), Color.rojo) - self.assertEqual(Color['rojo'], Color.red) - self.assertEqual(len(Color), 3) - - def test_extend_enum_unique(self): - class Color(UniqueEnum): - red = 1 - green = 2 - blue = 3 - self.assertRaisesRegex(ValueError, r' is a duplicate of ', extend_enum, Color, 'rojo', 1) - # - self.assertEqual(Color.red.name, 'red') - self.assertEqual(Color.red.value, 1) - self.assertTrue(Color.red in Color) - self.assertEqual(Color(1), Color.red) - self.assertEqual(Color['red'], Color.red) - self.assertEqual(Color.green.name, 'green') - self.assertEqual(Color.green.value, 2) - self.assertTrue(Color.green in Color) - self.assertEqual(Color(2), Color.green) - self.assertEqual(Color['blue'], Color.blue) - self.assertEqual(Color.blue.name, 'blue') - self.assertEqual(Color.blue.value, 3) - self.assertTrue(Color.blue in Color) - self.assertEqual(Color(3), Color.blue) - self.assertEqual(len(Color), 3) - # - extend_enum(Color, 'brown', 4) - self.assertEqual(Color.brown.name, 'brown') - self.assertEqual(Color.brown.value, 4) - self.assertTrue(Color.brown in Color) - self.assertEqual(Color(4), Color.brown) - self.assertEqual(Color['brown'], Color.brown) - self.assertEqual(len(Color), 4) - # - self.assertRaisesRegex(ValueError, '', extend_enum, Color, 'verde', 2) - # - extend_enum(Color, 'mauve') - self.assertEqual(Color.mauve.name, 'mauve') - self.assertEqual(Color.mauve.value, 5) - self.assertTrue(Color.mauve in Color) - self.assertEqual(Color(5), Color.mauve) - self.assertEqual(Color['mauve'], Color.mauve) - self.assertEqual(len(Color), 5) - - - def test_extend_enum_shadow_property(self): - class Color(Enum): - red = 1 - green = 2 - blue = 3 - extend_enum(Color, 'value', 4) - self.assertEqual(Color.value.name, 'value') - self.assertEqual(Color.value.value, 4) - self.assertTrue(Color.value in Color) - self.assertEqual(Color(4), Color.value) - self.assertEqual(Color['value'], Color.value) - self.assertEqual(len(Color), 4) - self.assertEqual(Color.red.value, 1) - - def test_extend_enum_shadow_base(self): - class hohum(object): - def cyan(self): - "cyanize a color" - return self.value - class Color(hohum, Enum): - red = 1 - green = 2 - blue = 3 - self.assertRaisesRegex(TypeError, r'already in use in superclass', extend_enum, Color, 'cyan', 4) - self.assertEqual(len(Color), 3) - self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) - - def test_extend_enum_multivalue(self): - class Color(MultiValueEnum): - red = 1, 4, 7 - green = 2, 5, 8 - blue = 3, 6, 9 - extend_enum(Color, 'brown', 10, 20) - self.assertEqual(Color.brown.name, 'brown') - self.assertEqual(Color.brown.value, 10) - self.assertTrue(Color.brown in Color) - self.assertEqual(Color(10), Color.brown) - self.assertEqual(Color(20), Color.brown) - self.assertEqual(Color['brown'], Color.brown) - self.assertEqual(len(Color), 4) - # - self.assertRaisesRegex(ValueError, 'no values specified for MultiValue enum', extend_enum, Color, 'mauve') - - def test_extend_enum_multivalue_alias(self): - class Color(MultiValueEnum): - red = 1, 4, 7 - green = 2, 5, 8 - blue = 3, 6, 9 - self.assertRaisesRegex(ValueError, r' is a duplicate of ', extend_enum, Color, 'rojo', 7) - self.assertEqual(Color.red.name, 'red') - self.assertEqual(Color.red.value, 1) - self.assertTrue(Color.red in Color) - self.assertEqual(Color(1), Color.red) - self.assertEqual(Color(4), Color.red) - self.assertEqual(Color(7), Color.red) - self.assertEqual(Color['red'], Color.red) - self.assertEqual(Color.green.name, 'green') - self.assertEqual(Color.green.value, 2) - self.assertTrue(Color.green in Color) - self.assertEqual(Color(2), Color.green) - self.assertEqual(Color(5), Color.green) - self.assertEqual(Color(8), Color.green) - self.assertEqual(Color['blue'], Color.blue) - self.assertEqual(Color.blue.name, 'blue') - self.assertEqual(Color.blue.value, 3) - self.assertTrue(Color.blue in Color) - self.assertEqual(Color(3), Color.blue) - self.assertEqual(Color(6), Color.blue) - self.assertEqual(Color(9), Color.blue) - self.assertEqual(len(Color), 3) - - def test_extend_enum_multivalue_str(self): - class M(str, MultiValueEnum): - VALUE_1 = 'value_1', 'VALUE_1' - VALUE_2 = 'value_2', 'VALUE_2' - VALUE_3 = 'value_3', 'VALUE_3' - self.assertTrue(M._member_type_ is str) - extend_enum(M, 'VALUE_4', 'value_4', 'VALUE_4') - self.assertEqual(list(M), [M.VALUE_1, M.VALUE_2, M.VALUE_3, M.VALUE_4]) - self.assertTrue(M('value_4') is M.VALUE_4) - self.assertTrue(M('VALUE_4') is M.VALUE_4) - self.assertTrue(M.VALUE_4.name == 'VALUE_4') - self.assertTrue(M.VALUE_4.value == 'value_4') - - def test_extend_intenum(self): - class Index(IntEnum): - DeviceType = 0x1000 - ErrorRegister = 0x1001 - - for name, value in ( - ('ControlWord', 0x6040), - ('StatusWord', 0x6041), - ('OperationMode', 0x6060), - ): - extend_enum(Index, name, value) - - self.assertEqual(len(Index), 5) - self.assertEqual(list(Index), [Index.DeviceType, Index.ErrorRegister, Index.ControlWord, Index.StatusWord, Index.OperationMode]) - self.assertEqual(Index.DeviceType.value, 0x1000) - self.assertEqual(Index.StatusWord.value, 0x6041) - - def test_extend_multi_init(self): - try: - from http import HTTPStatus - length = len(HTTPStatus) - except ImportError: - class HTTPStatus(IntEnum): - def __new__(cls, value, phrase, description): - obj = int.__new__(cls, value) - obj._value_ = value - - obj.phrase = phrase - obj.description = description - return obj - CONTINUE = 100, 'Continue', 'Request received, please continue' - SWITCHING_PROTOCOLS = 101, 'Switching Protocols', 'Switching to new protocol; obey Upgrade header' - PROCESSING = 102, 'Processing', '' - length = 3 - extend_enum(HTTPStatus, 'BAD_SPAM', 513, 'Too greasy', 'for a train') - extend_enum(HTTPStatus, 'BAD_EGGS', 514, 'Too green', '') - self.assertEqual(len(HTTPStatus), length+2) - self.assertEqual( - list(HTTPStatus)[-2:], - [HTTPStatus.BAD_SPAM, HTTPStatus.BAD_EGGS], - ) - self.assertEqual(HTTPStatus.BAD_SPAM.value, 513) - self.assertEqual(HTTPStatus.BAD_SPAM.name, 'BAD_SPAM') - self.assertEqual(HTTPStatus.BAD_SPAM.phrase, 'Too greasy') - self.assertEqual(HTTPStatus.BAD_SPAM.description, 'for a train') - self.assertEqual(HTTPStatus.BAD_EGGS.value, 514) - self.assertEqual(HTTPStatus.BAD_EGGS.name, 'BAD_EGGS') - self.assertEqual(HTTPStatus.BAD_EGGS.phrase, 'Too green') - self.assertEqual(HTTPStatus.BAD_EGGS.description, '') - - def test_extend_flag(self): - class Color(Flag): - BLACK = 0 - RED = 1 - GREEN = 2 - BLUE = 4 - extend_enum(Color, 'MAGENTA') - self.assertTrue(Color(8) is Color.MAGENTA) - self.assertTrue(isinstance(Color.MAGENTA, Color)) - self.assertEqual(Color.MAGENTA.value, 8) - extend_enum(Color, 'PURPLE', 11) - self.assertTrue(Color(11) is Color.PURPLE) - self.assertTrue(isinstance(Color.PURPLE, Color)) - self.assertEqual(Color.PURPLE.value, 11) - self.assertTrue(issubclass(Color, Flag)) - - def test_extend_flag_backwards(self): - class Color(Flag): - BLACK = 0 - RED = 1 - GREEN = 2 - BLUE = 4 - extend_enum(Color, 'PURPLE', 11) - self.assertTrue(Color(11) is Color.PURPLE) - self.assertTrue(isinstance(Color.PURPLE, Color)) - self.assertEqual(Color.PURPLE.value, 11) - self.assertTrue(issubclass(Color, Flag)) - # - extend_enum(Color, 'MAGENTA') - self.assertTrue(Color(8) is Color.MAGENTA) - self.assertTrue(isinstance(Color.MAGENTA, Color)) - self.assertEqual(Color.MAGENTA.value, 8) - # - extend_enum(Color, 'mauve') - self.assertEqual(Color.mauve.name, 'mauve') - self.assertEqual(Color.mauve.value, 16) - self.assertTrue(Color.mauve in Color) - self.assertEqual(Color(16), Color.mauve) - self.assertEqual(Color['mauve'], Color.mauve) - self.assertEqual(len(Color), 5) - - def test_extend_intflag(self): - class Color(IntFlag): - BLACK = 0 - RED = 1 - GREEN = 2 - BLUE = 4 - extend_enum(Color, 'MAGENTA') - self.assertTrue(Color(8) is Color.MAGENTA) - self.assertTrue(isinstance(Color.MAGENTA, Color)) - self.assertEqual(Color.MAGENTA.value, 8) - extend_enum(Color, 'PURPLE', 11) - self.assertTrue(Color(11) is Color.PURPLE) - self.assertTrue(isinstance(Color.PURPLE, Color)) - self.assertEqual(Color.PURPLE.value, 11) - self.assertTrue(issubclass(Color, Flag)) - # - extend_enum(Color, 'mauve') - self.assertEqual(Color.mauve.name, 'mauve') - self.assertEqual(Color.mauve.value, 16) - self.assertTrue(Color.mauve in Color) - self.assertEqual(Color(16), Color.mauve) - self.assertEqual(Color['mauve'], Color.mauve) - self.assertEqual(len(Color), 5) - - def test_extend_intflag_backwards(self): - class Color(IntFlag): - BLACK = 0 - RED = 1 - GREEN = 2 - BLUE = 4 - extend_enum(Color, 'PURPLE', 11) - self.assertTrue(Color(11) is Color.PURPLE) - self.assertTrue(isinstance(Color.PURPLE, Color)) - self.assertEqual(Color.PURPLE.value, 11) - self.assertTrue(issubclass(Color, Flag)) - # - extend_enum(Color, 'MAGENTA') - self.assertTrue(Color(8) is Color.MAGENTA) - self.assertTrue(isinstance(Color.MAGENTA, Color)) - self.assertEqual(Color.MAGENTA.value, 8) - # - extend_enum(Color, 'mauve') - self.assertEqual(Color.mauve.name, 'mauve') - self.assertEqual(Color.mauve.value, 16) - self.assertTrue(Color.mauve in Color) - self.assertEqual(Color(16), Color.mauve) - self.assertEqual(Color['mauve'], Color.mauve) - self.assertEqual(len(Color), 5) - - def test_extend_strenum(self): - class Color(StrEnum): - RED = auto() - GREEN = auto() - BLUE = auto() - extend_enum(Color, 'BLACK') - self.assertEqual(Color.BLACK.name, 'BLACK') - self.assertEqual(Color.BLACK.value, 'black') - self.assertEqual(len(Color), 4) - - -class TestIssues(TestCase): - - def test_auto_multi_int(self): - class Measurement(int, MultiValueEnum, AddValueEnum): - _order_ = 'one two three' - _start_ = 0 - one = "20110721" - two = "20120911" - three = "20110518" - self.assertEqual([m.value for m in Measurement], [0, 1, 2]) - self.assertEqual([m.name for m in Measurement], ['one', 'two', 'three']) - self.assertIs(Measurement('20110721'), Measurement.one) - self.assertIs(Measurement(0), Measurement.one) - self.assertIs(Measurement('20120911'), Measurement.two) - self.assertIs(Measurement(1), Measurement.two) - self.assertIs(Measurement('20110518'), Measurement.three) - self.assertIs(Measurement(2), Measurement.three) - - def test_auto_kwds(self): - class Item(Enum): - _order_ = 'A B' - A = auto(size=100, requirements={}) - B = auto(size=200, requirements={A: 1}) - # - def __new__(cls, value, size, requirements): - obj = object.__new__(cls) - obj._value_ = value - obj.size = size - # fix requirements - new_requirements = {} - for k, v in requirements.items(): - if isinstance(k, auto): - k = k.enum_member - new_requirements[k] = v - obj.requirements = new_requirements - return obj - self.assertEqual((Item.A.value, Item.A.size, Item.A.requirements), (1, 100, {})) - self.assertEqual((Item.B.value, Item.B.size, Item.B.requirements), (2, 200, {Item.A: 1})) - - def test_extend_flag(self): - class FlagTest(Flag): # Or IntFlag - NONE = 0 - LOW = 1 - MID = 2 - extend_enum(FlagTest, 'HIGH', 4) - self.assertEqual(FlagTest.LOW | FlagTest.HIGH, FlagTest(5)) - self.assertEqual((FlagTest.LOW | FlagTest.HIGH).value, 5) - - def test_extend_unhashable(self): - class TestEnum(Enum): - ABC = { - 'id': 0, - 'value': 'abc' - } - DEF = { - 'id': 1, - 'value': 'def' - } - rand = uuid.uuid4().hex - new_value = { - 'id': 99, - 'value': 'new', - } - extend_enum(TestEnum, rand, new_value) - - - -# Test conversion of global constants -# These are unordered here on purpose to ensure that declaration order -# makes no difference. -CONVERT_TEST_NAME_D = 5 -CONVERT_TEST_NAME_C = 5 -CONVERT_TEST_NAME_B = 5 -CONVERT_TEST_NAME_A = 5 # This one should sort first. -CONVERT_TEST_NAME_E = 5 -CONVERT_TEST_NAME_F = 5 -CONVERT_TEST_SIGABRT = 4 # and this one -CONVERT_TEST_SIGIOT = 4 -CONVERT_TEST_EIO = 7 -CONVERT_TEST_EBUS = 7 # and this one - -CONVERT_STRING_TEST_NAME_D = 5 -CONVERT_STRING_TEST_NAME_C = 5 -CONVERT_STRING_TEST_NAME_B = 5 -CONVERT_STRING_TEST_NAME_A = 5 # This one should sort first. -CONVERT_STRING_TEST_NAME_E = 5 -CONVERT_STRING_TEST_NAME_F = 5 - -# global names for StrEnum._convert_ test -CONVERT_STR_TEST_2 = 'goodbye' -CONVERT_STR_TEST_1 = 'hello' - -# We also need values that cannot be compared: -UNCOMPARABLE_A = 5 -UNCOMPARABLE_C = (9, 1) # naming order is broken on purpose -UNCOMPARABLE_B = 'value' - -COMPLEX_C = 1j -COMPLEX_A = 2j -COMPLEX_B = 3j - - -class TestConvert(TestCase): - - def tearDown(self): - # Reset the module-level test variables to their original integer - # values, otherwise the already created enum values get converted - # instead. - g = globals() - for suffix in ['A', 'B', 'C', 'D', 'E', 'F']: - g['CONVERT_TEST_NAME_%s' % suffix] = 5 - g['CONVERT_STRING_TEST_NAME_%s' % suffix] = 5 - for suffix, value in (('A', 5), ('B', (9, 1)), ('C', 'value')): - g['UNCOMPARABLE_%s' % suffix] = value - for suffix, value in (('A', 2j), ('B', 3j), ('C', 1j)): - g['COMPLEX_%s' % suffix] = value - for suffix, value in (('1', 'hello'), ('2', 'goodbye')): - g['CONVERT_STR_TEST_%s' % suffix] = value - g['CONVERT_TEST_SIGABRT'] = 4 - g['CONVERT_TEST_SIGIOT'] = 4 - g['CONVERT_TEST_EIO'] = 7 - g['CONVERT_TEST_EBUS'] = 7 - - def test_convert_value_lookup_priority(self): - test_type = IntEnum._convert_( - 'UnittestConvert', - MODULE, - filter=lambda x: x.startswith('CONVERT_TEST_')) - # We don't want the reverse lookup value to vary when there are - # multiple possible names for a given value. It should always - # report the first lexigraphical name in that case. - self.assertEqual(test_type(5).name, 'CONVERT_TEST_NAME_A') - self.assertEqual(test_type(4).name, 'CONVERT_TEST_SIGABRT') - self.assertEqual(test_type(7).name, 'CONVERT_TEST_EBUS') - self.assertEqual( - list(test_type), - [ - test_type.CONVERT_TEST_SIGABRT, - test_type.CONVERT_TEST_NAME_A, - test_type.CONVERT_TEST_EBUS, - ], - ) - - def test_convert_int(self): - test_type = IntEnum._convert_( - 'UnittestConvert', - MODULE, - filter=lambda x: x.startswith('CONVERT_TEST_')) - # Ensure that test_type has all of the desired names and values. - self.assertEqual(test_type.CONVERT_TEST_NAME_F, - test_type.CONVERT_TEST_NAME_A) - self.assertEqual(test_type.CONVERT_TEST_NAME_B, 5) - self.assertEqual(test_type.CONVERT_TEST_NAME_C, 5) - self.assertEqual(test_type.CONVERT_TEST_NAME_D, 5) - self.assertEqual(test_type.CONVERT_TEST_NAME_E, 5) - # Ensure that test_type only picked up names matching the filter. - int_dir = dir(int) + [ - 'CONVERT_TEST_NAME_A', 'CONVERT_TEST_NAME_B', 'CONVERT_TEST_NAME_C', - 'CONVERT_TEST_NAME_D', 'CONVERT_TEST_NAME_E', 'CONVERT_TEST_NAME_F', - 'CONVERT_TEST_SIGABRT', 'CONVERT_TEST_SIGIOT', - 'CONVERT_TEST_EIO', 'CONVERT_TEST_EBUS', - ] - extra = [name for name in dir(test_type) if name not in enum_dir(test_type)] - missing = [name for name in enum_dir(test_type) if name not in dir(test_type)] - self.assertEqual( - extra + missing, - [], - msg='extra names: %r; missing names: %r' % (extra, missing), - ) - - @unittest.skipUnless(PY3, 'everything is comparable on Python 2') - def test_convert_uncomparable(self): - uncomp = Enum._convert_( - 'Uncomparable', - MODULE, - filter=lambda x: x.startswith('UNCOMPARABLE_')) - # Should be ordered by `name` only: - self.assertEqual( - list(uncomp), - [uncomp.UNCOMPARABLE_A, uncomp.UNCOMPARABLE_B, uncomp.UNCOMPARABLE_C], - list(uncomp), - ) - - @unittest.skipUnless(PY3, 'everything is comparable on Python 2') - def test_convert_complex(self): - uncomp = Enum._convert_( - 'Uncomparable', - MODULE, - filter=lambda x: x.startswith('COMPLEX_')) - # Should be ordered by `name` only: - self.assertEqual( - list(uncomp), - [uncomp.COMPLEX_A, uncomp.COMPLEX_B, uncomp.COMPLEX_C], - ) - - def test_convert_str(self): - test_type = StrEnum._convert_( - 'UnittestConvert', - MODULE, - filter=lambda x: x.startswith('CONVERT_STR_'), - as_global=True) - # Ensure that test_type has all of the desired names and values. - self.assertEqual(test_type.CONVERT_STR_TEST_1, 'hello') - self.assertEqual(test_type.CONVERT_STR_TEST_2, 'goodbye') - # Ensure that test_type only picked up names matching the filter. - extra = [name for name in dir(test_type) if name not in enum_dir(test_type)] - missing = [name for name in enum_dir(test_type) if name not in dir(test_type)] - self.assertEqual( - extra + missing, - [], - msg='extra names: %r; missing names: %r' % (extra, missing), - ) - self.assertEqual(repr(test_type.CONVERT_STR_TEST_1), '%s.CONVERT_STR_TEST_1' % SHORT_MODULE) - self.assertEqual(str(test_type.CONVERT_STR_TEST_2), 'goodbye') - self.assertEqual(format(test_type.CONVERT_STR_TEST_1), 'hello') - - def test_convert_repr_and_str(self): - test_type = IntEnum._convert_( - 'UnittestConvert', - MODULE, - filter=lambda x: x.startswith('CONVERT_STRING_TEST_'), - as_global=True) - self.assertEqual(repr(test_type.CONVERT_STRING_TEST_NAME_A), '%s.CONVERT_STRING_TEST_NAME_A' % SHORT_MODULE) - self.assertEqual(str(test_type.CONVERT_STRING_TEST_NAME_A), '5') - self.assertEqual(format(test_type.CONVERT_STRING_TEST_NAME_A), '5') - -# helpers - -def enum_dir(cls): - interesting = set(cls._member_names_ + [ - '__class__', '__contains__', '__doc__', '__getitem__', - '__iter__', '__len__', '__members__', '__module__', - '__name__', - ]) - if cls._new_member_ is not object.__new__: - interesting.add('__new__') - if cls.__init_subclass__ is not Enum.__init_subclass__: - interesting.add('__init_subclass__') - if hasattr(object, '__qualname__'): - interesting.add('__qualname__') - for method in ('__init__', '__format__', '__repr__', '__str__'): - if getattr(cls, method) not in (getattr(Enum, method), getattr(Flag, method)): - interesting.add(method) - if cls._member_type_ is object: - return sorted(interesting) - else: - # return whatever mixed-in data type has - return sorted(set(dir(cls._member_type_)) | interesting) - -def member_dir(member): - if member.__class__._member_type_ is object: - allowed = set(['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'value']) - else: - allowed = set(dir(member)) - for cls in member.__class__.mro(): - for name, obj in cls.__dict__.items(): - if name[0] == '_': - continue - if isinstance(obj, enum.property): - if obj.fget is not None or name not in member._member_map_: - allowed.add(name) - else: - allowed.discard(name) - else: - allowed.add(name) - return sorted(allowed) - - - -if __name__ == '__main__': - tempdir = tempfile.mkdtemp() - test = None - try: - if PY3: - test_v3.tempdir = tempdir - test = unittest.main(exit=False) - sys.stdout.flush() - for name, reason in test.result.skipped: - print("%s: %s" % (name, reason)) - finally: - shutil.rmtree(tempdir, True) - if test: - sys.exit(len(test.result.errors or test.result.failures) and 1 or 0) - diff --git a/venv/Lib/site-packages/aenum/test_v3.py b/venv/Lib/site-packages/aenum/test_v3.py deleted file mode 100644 index 62453df1..00000000 --- a/venv/Lib/site-packages/aenum/test_v3.py +++ /dev/null @@ -1,1982 +0,0 @@ -from . import EnumMeta, Enum, IntEnum, Flag, IntFlag, StrEnum, UniqueEnum, AutoEnum, AddValueEnum -from . import NamedTuple, TupleSize, MagicValue, AddValue, NoAlias, Unique, MultiValue -from . import AutoNumberEnum,MultiValueEnum, OrderedEnum, unique, skip, extend_enum, auto -from . import StdlibEnumMeta, StdlibEnum, StdlibIntEnum, StdlibFlag, StdlibIntFlag, StdlibStrEnum -from . import pyver, PY3_3, PY3_4, PY3_5, PY3_6, PY3_11 -from . import add_stdlib_integration, remove_stdlib_integration - -from collections import OrderedDict -from datetime import timedelta -from pickle import dumps, loads, PicklingError, HIGHEST_PROTOCOL -from unittest import TestCase, main - -import os -import sys -import tempfile -import textwrap -import unittest - -try: - import pyparsing -except (ImportError, SyntaxError): - pyparsing = None - -try: - RecursionError -except NameError: - # python3.4 - RecursionError = RuntimeError - -class TestEnumV3(TestCase): - - def setUp(self): - class Season(Enum): - SPRING = 1 - SUMMER = 2 - AUTUMN = 3 - WINTER = 4 - self.Season = Season - - class Konstants(float, Enum): - E = 2.7182818 - PI = 3.1415926 - TAU = 2 * PI - self.Konstants = Konstants - - class Grades(IntEnum): - A = 5 - B = 4 - C = 3 - D = 2 - F = 0 - self.Grades = Grades - - class Directional(str, Enum): - EAST = 'east' - WEST = 'west' - NORTH = 'north' - SOUTH = 'south' - self.Directional = Directional - - from datetime import date - class Holiday(date, Enum): - NEW_YEAR = 2013, 1, 1 - IDES_OF_MARCH = 2013, 3, 15 - self.Holiday = Holiday - - @unittest.skipUnless(StdlibEnumMeta, 'Stdlib enum not available') - def test_stdlib_inheritence(self): - # 3.4 - self.assertTrue(issubclass(self.Season, StdlibEnum)) - self.assertTrue(isinstance(self.Season.SPRING, StdlibEnum)) - # - if pyver >= PY3_6: - class AFlag(Flag): - one = 1 - self.assertTrue(issubclass(AFlag, StdlibEnum)) - self.assertTrue(isinstance(AFlag.one, StdlibEnum)) - self.assertTrue(issubclass(AFlag, StdlibFlag)) - self.assertTrue(isinstance(AFlag.one, StdlibFlag)) - # - class AnIntFlag(IntFlag): - one = 1 - self.assertTrue(issubclass(AnIntFlag, StdlibEnum)) - self.assertTrue(isinstance(AnIntFlag.one, StdlibEnum)) - self.assertTrue(issubclass(AnIntFlag, StdlibFlag)) - self.assertTrue(isinstance(AnIntFlag.one, StdlibFlag)) - self.assertTrue(issubclass(AnIntFlag, StdlibIntFlag)) - self.assertTrue(isinstance(AnIntFlag.one, StdlibIntFlag)) - # - if pyver >= PY3_11: - class AStrEnum(StrFlag): - one = '1' - self.assertTrue(issubclass(AStrEnum, StdlibEnum)) - self.assertTrue(isinstance(AStrEnum.one, StdlibEnum)) - self.assertTrue(issubclass(AStrEnum, StdlibStrEnum)) - self.assertTrue(isinstance(AStrEnum.one, StdlibStrEnum)) - - @unittest.skipUnless(StdlibEnumMeta, 'Stdlib enum not available') - def test_stdlib_bad_getattribute(self): - class BadEnumType(StdlibEnumMeta): - def __getattribute__(cls, name): - obj = super().__getattribute__(name) - if isinstance(obj, cls): - obj.deprecate() - return obj - with self.assertRaisesRegex(RecursionError, 'endless recursion'): - class BaseEnum(StdlibEnum): - pass - class BadEnum(BaseEnum, metaclass=BadEnumType): - FOO = 'bar' - try: - remove_stdlib_integration() - class OkayEnum(StdlibEnum, metaclass=BadEnumType): - FOO = 'bar' - finally: - add_stdlib_integration() - - @unittest.skipUnless(pyver >= PY3_5, '__qualname__ requires python 3.5 or greater') - def test_pickle_enum_function_with_qualname(self): - Theory = Enum('Theory', 'rule law supposition', qualname='spanish_inquisition') - globals()['spanish_inquisition'] = Theory - test_pickle_dump_load(self.assertTrue, Theory.rule) - test_pickle_dump_load(self.assertTrue, Theory) - - def test_auto_init(self): - class Planet(Enum, init='mass radius'): - MERCURY = (3.303e+23, 2.4397e6) - VENUS = (4.869e+24, 6.0518e6) - EARTH = (5.976e+24, 6.37814e6) - MARS = (6.421e+23, 3.3972e6) - JUPITER = (1.9e+27, 7.1492e7) - SATURN = (5.688e+26, 6.0268e7) - URANUS = (8.686e+25, 2.5559e7) - NEPTUNE = (1.024e+26, 2.4746e7) - @property - def surface_gravity(self): - # universal gravitational constant (m3 kg-1 s-2) - G = 6.67300E-11 - return G * self.mass / (self.radius * self.radius) - self.assertEqual(round(Planet.EARTH.surface_gravity, 2), 9.80) - self.assertEqual(Planet.EARTH.value, (5.976e+24, 6.37814e6)) - - def test_auto_init_with_value(self): - class Color(Enum, init='value, rgb'): - RED = 1, (1, 0, 0) - BLUE = 2, (0, 1, 0) - GREEN = 3, (0, 0, 1) - self.assertEqual(Color.RED.value, 1) - self.assertEqual(Color.BLUE.value, 2) - self.assertEqual(Color.GREEN.value, 3) - self.assertEqual(Color.RED.rgb, (1, 0, 0)) - self.assertEqual(Color.BLUE.rgb, (0, 1, 0)) - self.assertEqual(Color.GREEN.rgb, (0, 0, 1)) - - def test_auto_turns_off(self): - with self.assertRaises(NameError): - class Color(Enum, settings=MagicValue): - red - green - blue - def hello(self): - print('Hello! My serial is %s.' % self.value) - rose - with self.assertRaises(NameError): - class Color(Enum, settings=MagicValue): - red - green - blue - def __init__(self, *args): - pass - rose - - def test_magic(self): - class Color(Enum, settings=MagicValue): - red, green, blue - self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) - self.assertEqual(Color.red.value, 1) - - def test_ignore_not_overridden(self): - with self.assertRaisesRegex(TypeError, 'object is not callable'): - class Color(Flag): - _ignore_ = 'irrelevent' - _settings_ = MagicValue - @property - def shade(self): - print('I am light', self.name.lower()) - - def test_magic_start(self): - class Color(Enum, settings=MagicValue, start=0): - red, green, blue - self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) - self.assertEqual(Color.red.value, 0) - - def test_dir_on_class(self): - Season = self.Season - self.assertEqual( - set(dir(Season)), - set(['__class__', '__doc__', '__members__', '__module__', - 'SPRING', 'SUMMER', 'AUTUMN', 'WINTER', - '__init_subclass__', '__name__', '__getitem__', '__len__', - '__contains__', '__iter__', '__qualname__', - ])) - - def test_dir_on_item(self): - Season = self.Season - self.assertEqual( - set(dir(Season.WINTER)), - set(['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'value', 'values']), - ) - - def test_dir_with_added_behavior(self): - class Test(Enum): - this = 'that' - these = 'those' - def wowser(self): - return ("Wowser! I'm %s!" % self.name) - self.assertEqual( - set(dir(Test)), - set([ - '__class__', '__doc__', '__members__', '__module__', 'this', 'these', - '__init_subclass__', '__name__', '__getitem__', '__len__', - '__contains__', '__iter__', '__qualname__', - ])) - self.assertEqual( - set(dir(Test.this)), - set(['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'value', 'values', 'wowser']), - ) - - def test_dir_on_sub_with_behavior_on_super(self): - # see issue22506 - class SuperEnum(Enum): - def invisible(self): - return "did you see me?" - class SubEnum(SuperEnum): - sample = 5 - self.assertEqual( - set(dir(SubEnum.sample)), - set(['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'value', 'values', 'invisible']), - ) - - def test_members_are_always_ordered(self): - class AlwaysOrdered(Enum): - first = 1 - second = 2 - third = 3 - self.assertTrue(type(AlwaysOrdered.__members__) is OrderedDict) - - def test_comparisons(self): - def bad_compare(): - Season.SPRING > 4 - Season = self.Season - self.assertNotEqual(Season.SPRING, 1) - self.assertRaises(TypeError, bad_compare) - - class Part(Enum): - SPRING = 1 - CLIP = 2 - BARREL = 3 - - self.assertNotEqual(Season.SPRING, Part.SPRING) - def bad_compare(): - Season.SPRING < Part.CLIP - self.assertRaises(TypeError, bad_compare) - - def test_duplicate_name(self): - with self.assertRaises(TypeError): - class Color1(Enum): - red = 1 - green = 2 - blue = 3 - red = 4 - - with self.assertRaises(TypeError): - class Color2(Enum): - red = 1 - green = 2 - blue = 3 - def red(self): - return 'red' - - with self.assertRaises(TypeError): - class Color3(Enum): - @property - def red(self): - return 'redder' - red = 1 - green = 2 - blue = 3 - - def test_duplicate_value_with_unique(self): - with self.assertRaises(ValueError): - class Color(Enum, settings=Unique): - red = 1 - green = 2 - blue = 3 - rojo = 1 - - def test_duplicate_value_with_noalias(self): - class Color(Enum, settings=NoAlias): - red = 1 - green = 2 - blue = 3 - rojo = 1 - self.assertFalse(Color.red is Color.rojo) - self.assertEqual(Color.red.value, 1) - self.assertEqual(Color.rojo.value, 1) - self.assertEqual(len(Color), 4) - self.assertEqual(list(Color), [Color.red, Color.green, Color.blue, Color.rojo]) - - def test_noalias_value_lookup(self): - class Color(Enum, settings=NoAlias): - red = 1 - green = 2 - blue = 3 - rojo = 1 - self.assertRaises(TypeError, Color, 2) - - def test_multivalue(self): - class Color(Enum, settings=MultiValue): - red = 1, 'red' - green = 2, 'green' - blue = 3, 'blue' - self.assertEqual(Color.red.value, 1) - self.assertIs(Color('green'), Color.green) - self.assertEqual(Color.blue.values, (3, 'blue')) - - def test_multivalue_with_duplicate_values(self): - with self.assertRaises(ValueError): - class Color(Enum, settings=MultiValue): - red = 1, 'red' - green = 2, 'green' - blue = 3, 'blue', 'red' - - def test_multivalue_with_duplicate_values_and_noalias(self): - with self.assertRaises(TypeError): - class Color(Enum, settings=(MultiValue, NoAlias)): - red = 1, 'red' - green = 2, 'green' - blue = 3, 'blue', 'red' - - def test_multivalue_and_auto(self): - with self.assertRaisesRegex(TypeError, r'MultiValue and MagicValue are mutually exclusive'): - class Color(Enum, settings=(MultiValue, MagicValue)): - red - green = 3, 'green' - blue - - def test_autonumber_and_init(self): - class Field(IntEnum, settings=AddValue, init='__doc__'): - TYPE = "Char, Date, Logical, etc." - START = "Field offset in record" - self.assertEqual(Field.TYPE, 1) - self.assertEqual(Field.START, 2) - self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.') - self.assertEqual(Field.START.__doc__, 'Field offset in record') - self.assertFalse(hasattr(Field, '_order_')) - - def test_autovalue_and_init(self): - class Field(IntEnum, init='value __doc__'): - TYPE = "Char, Date, Logical, etc." - START = "Field offset in record" - self.assertEqual(Field.TYPE, 1) - self.assertEqual(Field.START.__doc__, 'Field offset in record') - - def test_autonumber_and_start(self): - class Field(IntEnum, init='__doc__', settings=AddValue, start=0): - TYPE = "Char, Date, Logical, etc." - START = "Field offset in record" - self.assertEqual(Field.TYPE, 0) - self.assertEqual(Field.START, 1) - self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.') - self.assertEqual(Field.START.__doc__, 'Field offset in record') - - def test_autonumber_and_init_and_some_values(self): - class Field(IntEnum, init='value __doc__'): - TYPE = "Char, Date, Logical, etc." - START = "Field offset in record" - BLAH = 5, "test blah" - BELCH = 'test belch' - self.assertEqual(Field.TYPE, 1) - self.assertEqual(Field.START, 2) - self.assertEqual(Field.BLAH, 5) - self.assertEqual(Field.BELCH, 6) - self.assertEqual(Field.TYPE.__doc__, 'Char, Date, Logical, etc.') - self.assertEqual(Field.START.__doc__, 'Field offset in record') - self.assertEqual(Field.BLAH.__doc__, 'test blah') - self.assertEqual(Field.BELCH.__doc__, 'test belch') - - def test_autonumber_with_irregular_values(self): - class Point(AutoNumberEnum, init='x y'): - first = 7, 9 - second = 11, 13 - self.assertEqual(Point.first.value, 1) - self.assertEqual(Point.first.x, 7) - self.assertEqual(Point.first.y, 9) - self.assertEqual(Point.second.value, 2) - self.assertEqual(Point.second.x, 11) - self.assertEqual(Point.second.y, 13) - with self.assertRaisesRegex(TypeError, '.*number of fields provided do not match init ...x., .y.. != .3, 11, 13..'): - class Point(AutoNumberEnum, init='x y'): - first = 7, 9 - second = 3, 11, 13 - class Color(AutoNumberEnum, init='__doc__'): - # interactions between AutoNumberEnum and _generate_next_value_ may not be pretty - red = () - green = 'red' - blue = () - self.assertTrue(Color.red.__doc__, 1) - self.assertEqual(Color.green.__doc__, 'red') - self.assertTrue(Color.blue.__doc__, 2) - - def test_autonumber_and_property(self): - with self.assertRaises(TypeError): - class Color(AutoEnum): - _ignore_ = () - red = () - green = () - blue = () - @property - def cap_name(self) -> str: - return self.name.title() - - def test_autoenum(self): - class Color(AutoEnum): - red - green - blue - self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) - self.assertEqual([m.value for m in Color], [1, 2, 3]) - self.assertEqual([m.name for m in Color], ['red', 'green', 'blue']) - - def test_autoenum_with_str(self): - class Color(AutoEnum): - def _generate_next_value_(name, start, count, last_values): - return name - red - green - blue - self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) - self.assertEqual([m.value for m in Color], ['red', 'green', 'blue']) - self.assertEqual([m.name for m in Color], ['red', 'green', 'blue']) - - def test_autoenum_and_default_ignore(self): - class Color(AutoEnum): - red - green - blue - @property - def cap_name(self): - return self.name.title() - self.assertEqual(Color.blue.cap_name, 'Blue') - - def test_autonumber_and_overridden_ignore(self): - with self.assertRaises(TypeError): - class Color(AutoEnum): - _ignore_ = 'staticmethod' - red - green - blue - @property - def cap_name(self) -> str: - return self.name.title() - - def test_autonumber_and_multiple_assignment(self): - class Color(AutoEnum): - _ignore_ = 'property' - red - green - blue = cyan - @property - def cap_name(self) -> str: - return self.name.title() - self.assertEqual(Color.blue.cap_name, 'Cyan') - - def test_multivalue_and_autonumber_inherited(self): - class Measurement(int, Enum, settings=(MultiValue, AddValue), start=0): - one = "20110721" - two = "20120911" - three = "20110518" - M = Measurement - self.assertEqual(M.one, 0) - self.assertTrue(M.one is M(0) is M('20110721')) - - def test_combine_new_settings_with_old_settings(self): - class Auto(Enum, settings=Unique): - pass - with self.assertRaises(ValueError): - class AutoUnique(Auto, settings=MagicValue): - BLAH - BLUH - ICK = 1 - - def test_timedelta(self): - class Period(timedelta, Enum): - ''' - different lengths of time - ''' - _init_ = 'value period' - _settings_ = NoAlias - _ignore_ = 'Period i' - Period = vars() - for i in range(31): - Period['day_%d' % i] = i, 'day' - for i in range(15): - Period['week_%d' % i] = i*7, 'week' - for i in range(12): - Period['month_%d' % i] = i*30, 'month' - OneDay = day_1 - OneWeek = week_1 - self.assertFalse(hasattr(Period, '_ignore_')) - self.assertFalse(hasattr(Period, 'Period')) - self.assertFalse(hasattr(Period, 'i')) - self.assertTrue(isinstance(Period.day_1, timedelta)) - - def test_extend_enum_plain(self): - class Color(UniqueEnum): - red = 1 - green = 2 - blue = 3 - extend_enum(Color, 'brown', 4) - self.assertEqual(Color.brown.name, 'brown') - self.assertEqual(Color.brown.value, 4) - self.assertTrue(Color.brown in Color) - self.assertEqual(len(Color), 4) - - def test_extend_enum_shadow(self): - class Color(UniqueEnum): - red = 1 - green = 2 - blue = 3 - extend_enum(Color, 'value', 4) - self.assertEqual(Color.value.name, 'value') - self.assertEqual(Color.value.value, 4) - self.assertTrue(Color.value in Color) - self.assertEqual(len(Color), 4) - self.assertEqual(Color.red.value, 1) - - def test_extend_enum_generate(self): - class Foo(AutoEnum): - def _generate_next_value_(name, start, count, values, *args, **kwds): - return name - a - b - # - extend_enum(Foo, 'c') - self.assertEqual(Foo.a.value, 'a') - self.assertEqual(Foo.b.value, 'b') - self.assertEqual(Foo.c.value, 'c') - - def test_extend_enum_unique_with_duplicate(self): - with self.assertRaises(ValueError): - class Color(Enum, settings=Unique): - red = 1 - green = 2 - blue = 3 - extend_enum(Color, 'value', 1) - - def test_extend_enum_multivalue_with_duplicate(self): - with self.assertRaises(ValueError): - class Color(Enum, settings=MultiValue): - red = 1, 'rojo' - green = 2, 'verde' - blue = 3, 'azul' - extend_enum(Color, 'value', 2) - - def test_extend_enum_noalias_with_duplicate(self): - class Color(Enum, settings=NoAlias): - red = 1 - green = 2 - blue = 3 - extend_enum(Color, 'value', 3, ) - self.assertRaises(TypeError, Color, 3) - self.assertFalse(Color.value is Color.blue) - self.assertTrue(Color.value.value, 3) - - def test_no_duplicates(self): - def bad_duplicates(): - class Color(UniqueEnum): - red = 1 - green = 2 - blue = 3 - class Color(UniqueEnum): - red = 1 - green = 2 - blue = 3 - grene = 2 - self.assertRaises(ValueError, bad_duplicates) - - def test_no_duplicates_kinda(self): - class Silly(UniqueEnum): - one = 1 - two = 'dos' - name = 3 - class Sillier(IntEnum, UniqueEnum): - single = 1 - name = 2 - triple = 3 - value = 4 - - def test_auto_number(self): - class Color(Enum, settings=MagicValue): - red - blue - green - - self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) - self.assertEqual(Color.red.value, 1) - self.assertEqual(Color.blue.value, 2) - self.assertEqual(Color.green.value, 3) - - def test_auto_name(self): - class Color(Enum, settings=MagicValue): - def _generate_next_value_(name, start, count, last): - return name - red - blue - green - - self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) - self.assertEqual(Color.red.value, 'red') - self.assertEqual(Color.blue.value, 'blue') - self.assertEqual(Color.green.value, 'green') - - def test_auto_name_inherit(self): - class AutoNameEnum(Enum): - def _generate_next_value_(name, start, count, last): - return name - class Color(AutoNameEnum, settings=MagicValue): - red - blue - green - - self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) - self.assertEqual(Color.red.value, 'red') - self.assertEqual(Color.blue.value, 'blue') - self.assertEqual(Color.green.value, 'green') - - def test_auto_garbage(self): - class Color(Enum): - _settings_ = MagicValue - red = 'red' - blue - self.assertEqual(Color.blue.value, 1) - - def test_auto_garbage_corrected(self): - class Color(Enum, settings=MagicValue): - red = 'red' - blue = 2 - green - - self.assertEqual(list(Color), [Color.red, Color.blue, Color.green]) - self.assertEqual(Color.red.value, 'red') - self.assertEqual(Color.blue.value, 2) - self.assertEqual(Color.green.value, 3) - - def test_duplicate_auto(self): - class Dupes(Enum, settings=MagicValue): - first = primero - second - third - self.assertEqual([Dupes.first, Dupes.second, Dupes.third], list(Dupes)) - - def test_order_as_function(self): - # first with _init_ - class TestSequence(Enum): - _init_ = 'value, sequence' - _order_ = lambda member: member.sequence - item_id = 'An$(1,6)', 0 # Item Code - company_id = 'An$(7,2)', 1 # Company Code - warehouse_no = 'An$(9,4)', 2 # Warehouse Number - company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY - key_type = 'Cn$(19,3)', 4 # Key Type = '1**' - available = 'Zn$(1,1)', 5 # Available? - contract_item = 'Bn(2,1)', 6 # Contract Item? - sales_category = 'Fn', 7 # Sales Category - gl_category = 'Rn$(5,1)', 8 # G/L Category - warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category - inv_units = 'Qn$(7,2)', 10 # Inv Units - for i, member in enumerate(TestSequence): - self.assertEqual(i, member.sequence) - ts = TestSequence - self.assertEqual(ts.item_id.name, 'item_id') - self.assertEqual(ts.item_id.value, 'An$(1,6)') - self.assertEqual(ts.item_id.sequence, 0) - self.assertEqual(ts.company_id.name, 'company_id') - self.assertEqual(ts.company_id.value, 'An$(7,2)') - self.assertEqual(ts.company_id.sequence, 1) - self.assertEqual(ts.warehouse_no.name, 'warehouse_no') - self.assertEqual(ts.warehouse_no.value, 'An$(9,4)') - self.assertEqual(ts.warehouse_no.sequence, 2) - self.assertEqual(ts.company.name, 'company') - self.assertEqual(ts.company.value, 'Hn$(13,6)') - self.assertEqual(ts.company.sequence, 3) - self.assertEqual(ts.key_type.name, 'key_type') - self.assertEqual(ts.key_type.value, 'Cn$(19,3)') - self.assertEqual(ts.key_type.sequence, 4) - self.assertEqual(ts.available.name, 'available') - self.assertEqual(ts.available.value, 'Zn$(1,1)') - self.assertEqual(ts.available.sequence, 5) - self.assertEqual(ts.contract_item.name, 'contract_item') - self.assertEqual(ts.contract_item.value, 'Bn(2,1)') - self.assertEqual(ts.contract_item.sequence, 6) - self.assertEqual(ts.sales_category.name, 'sales_category') - self.assertEqual(ts.sales_category.value, 'Fn') - self.assertEqual(ts.sales_category.sequence, 7) - self.assertEqual(ts.gl_category.name, 'gl_category') - self.assertEqual(ts.gl_category.value, 'Rn$(5,1)') - self.assertEqual(ts.gl_category.sequence, 8) - self.assertEqual(ts.warehouse_category.name, 'warehouse_category') - self.assertEqual(ts.warehouse_category.value, 'Sn$(6,1)') - self.assertEqual(ts.warehouse_category.sequence, 9) - self.assertEqual(ts.inv_units.name, 'inv_units') - self.assertEqual(ts.inv_units.value, 'Qn$(7,2)') - self.assertEqual(ts.inv_units.sequence, 10) - # and then without - class TestSequence(Enum): - _order_ = lambda member: member.value[1] - item_id = 'An$(1,6)', 0 # Item Code - company_id = 'An$(7,2)', 1 # Company Code - warehouse_no = 'An$(9,4)', 2 # Warehouse Number - company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY - key_type = 'Cn$(19,3)', 4 # Key Type = '1**' - available = 'Zn$(1,1)', 5 # Available? - contract_item = 'Bn(2,1)', 6 # Contract Item? - sales_category = 'Fn', 7 # Sales Category - gl_category = 'Rn$(5,1)', 8 # G/L Category - warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category - inv_units = 'Qn$(7,2)', 10 # Inv Units - for i, member in enumerate(TestSequence): - self.assertEqual(i, member.value[1]) - ts = TestSequence - self.assertEqual(ts.item_id.name, 'item_id') - self.assertEqual(ts.item_id.value, ('An$(1,6)', 0)) - self.assertEqual(ts.company_id.name, 'company_id') - self.assertEqual(ts.company_id.value, ('An$(7,2)', 1)) - self.assertEqual(ts.warehouse_no.name, 'warehouse_no') - self.assertEqual(ts.warehouse_no.value, ('An$(9,4)', 2)) - self.assertEqual(ts.company.name, 'company') - self.assertEqual(ts.company.value, ('Hn$(13,6)', 3)) - self.assertEqual(ts.key_type.name, 'key_type') - self.assertEqual(ts.key_type.value, ('Cn$(19,3)', 4)) - self.assertEqual(ts.available.name, 'available') - self.assertEqual(ts.available.value, ('Zn$(1,1)', 5)) - self.assertEqual(ts.contract_item.name, 'contract_item') - self.assertEqual(ts.contract_item.value, ('Bn(2,1)', 6)) - self.assertEqual(ts.sales_category.name, 'sales_category') - self.assertEqual(ts.sales_category.value, ('Fn', 7)) - self.assertEqual(ts.gl_category.name, 'gl_category') - self.assertEqual(ts.gl_category.value, ('Rn$(5,1)', 8)) - self.assertEqual(ts.warehouse_category.name, 'warehouse_category') - self.assertEqual(ts.warehouse_category.value, ('Sn$(6,1)', 9)) - self.assertEqual(ts.inv_units.name, 'inv_units') - self.assertEqual(ts.inv_units.value, ('Qn$(7,2)', 10)) - # then with _init_ but without value - with self.assertRaises(TypeError): - class TestSequence(Enum): - _init_ = 'sequence' - _order_ = lambda member: member.sequence - item_id = 'An$(1,6)', 0 # Item Code - company_id = 'An$(7,2)', 1 # Company Code - warehouse_no = 'An$(9,4)', 2 # Warehouse Number - company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY - key_type = 'Cn$(19,3)', 4 # Key Type = '1**' - available = 'Zn$(1,1)', 5 # Available? - contract_item = 'Bn(2,1)', 6 # Contract Item? - sales_category = 'Fn', 7 # Sales Category - gl_category = 'Rn$(5,1)', 8 # G/L Category - warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category - inv_units = 'Qn$(7,2)', 10 # Inv Units - # finally, out of order so Python 3 barfs - with self.assertRaises(TypeError): - class TestSequence(Enum): - _init_ = 'sequence' - _order_ = lambda member: member.sequence - item_id = 'An$(1,6)', 0 # Item Code - warehouse_no = 'An$(9,4)', 2 # Warehouse Number - company = 'Hn$(13,6)', 3 # 4 SPACES + COMPANY - company_id = 'An$(7,2)', 1 # Company Code - inv_units = 'Qn$(7,2)', 10 # Inv Units - available = 'Zn$(1,1)', 5 # Available? - contract_item = 'Bn(2,1)', 6 # Contract Item? - sales_category = 'Fn', 7 # Sales Category - key_type = 'Cn$(19,3)', 4 # Key Type = '1**' - gl_category = 'Rn$(5,1)', 8 # G/L Category - warehouse_category = 'Sn$(6,1)', 9 # Warehouse Category - - if pyver >= PY3_3: - def test_missing(self): - class Color(Enum): - red = 1 - green = 2 - blue = 3 - @classmethod - def _missing_(cls, item): - if item == 'three': - return cls.blue - elif item == 'bad return': - # trigger internal error - return 5 - elif item == 'error out': - raise ZeroDivisionError - else: - # trigger not found - return None - self.assertIs(Color('three'), Color.blue) - self.assertRaises(ValueError, Color, 7) - try: - Color('bad return') - except TypeError as exc: - self.assertTrue(isinstance(exc.__cause__, ValueError)) - else: - raise Exception('Exception not raised.') - try: - Color('error out') - except ZeroDivisionError as exc: - self.assertTrue(isinstance(exc.__cause__, ValueError)) - else: - raise Exception('Exception not raised.') - - def test_enum_of_types(self): - """Support using Enum to refer to types deliberately.""" - class MyTypes(Enum): - i = int - f = float - s = str - self.assertEqual(MyTypes.i.value, int) - self.assertEqual(MyTypes.f.value, float) - self.assertEqual(MyTypes.s.value, str) - class Foo: - pass - class Bar: - pass - class MyTypes2(Enum): - a = Foo - b = Bar - self.assertEqual(MyTypes2.a.value, Foo) - self.assertEqual(MyTypes2.b.value, Bar) - class SpamEnumNotInner: - pass - class SpamEnum(Enum): - spam = SpamEnumNotInner - self.assertEqual(SpamEnum.spam.value, SpamEnumNotInner) - - def test_nested_classes_in_enum_do_not_create_members(self): - """Support locally-defined nested classes.""" - # manually set __qualname__ to remove testing framework noise - class Outer(Enum): - __qualname__ = "Outer" - a = 1 - b = 2 - class Inner(Enum): - __qualname__ = "Outer.Inner" - foo = 10 - bar = 11 - self.assertTrue(isinstance(Outer.Inner, type)) - self.assertEqual(Outer.a.value, 1) - self.assertEqual(Outer.Inner.foo.value, 10) - self.assertEqual( - list(Outer.Inner), - [Outer.Inner.foo, Outer.Inner.bar], - ) - self.assertEqual( - list(Outer), - [Outer.a, Outer.b], - ) - - if pyver == PY3_4: - def test_class_nested_enum_and_pickle_protocol_four(self): - # would normally just have this directly in the class namespace - class NestedEnum(Enum): - twigs = 'common' - shiny = 'rare' - - self.__class__.NestedEnum = NestedEnum - self.NestedEnum.__qualname__ = '%s.NestedEnum' % self.__class__.__name__ - test_pickle_exception( - self.assertRaises, PicklingError, self.NestedEnum.twigs, - protocol=(0, 3)) - test_pickle_dump_load(self.assertTrue, self.NestedEnum.twigs, - protocol=(4, HIGHEST_PROTOCOL)) - - elif pyver >= PY3_5: - def test_class_nested_enum_and_pickle_protocol_four(self): - # would normally just have this directly in the class namespace - class NestedEnum(Enum): - twigs = 'common' - shiny = 'rare' - - self.__class__.NestedEnum = NestedEnum - self.NestedEnum.__qualname__ = '%s.NestedEnum' % self.__class__.__name__ - test_pickle_dump_load(self.assertTrue, self.NestedEnum.twigs, - protocol=(0, HIGHEST_PROTOCOL)) - - if pyver >= PY3_4: - def test_enum_injection(self): - class Color(Enum): - _order_ = 'BLACK WHITE' - BLACK = Color('black', '#000') - WHITE = Color('white', '#fff') - - def __init__(self, label, hex): - self.label = label - self.hex = hex - - self.assertEqual([Color.BLACK, Color.WHITE], list(Color)) - self.assertEqual(Color.WHITE.hex, '#fff') - self.assertEqual(Color.BLACK.label, 'black') - - def test_subclasses_with_getnewargs_ex(self): - class NamedInt(int): - __qualname__ = 'NamedInt' # needed for pickle protocol 4 - def __new__(cls, *args): - _args = args - if len(args) < 2: - raise TypeError("name and value must be specified") - name, args = args[0], args[1:] - self = int.__new__(cls, *args) - self._intname = name - self._args = _args - return self - def __getnewargs_ex__(self): - return self._args, {} - @property - def __name__(self): - return self._intname - def __repr__(self): - # repr() is updated to include the name and type info - return "{}({!r}, {})".format(type(self).__name__, - self.__name__, - int.__repr__(self)) - def __str__(self): - # str() is unchanged, even if it relies on the repr() fallback - base = int - base_str = base.__str__ - if base_str.__objclass__ is object: - return base.__repr__(self) - return base_str(self) - # for simplicity, we only define one operator that - # propagates expressions - def __add__(self, other): - temp = int(self) + int( other) - if isinstance(self, NamedInt) and isinstance(other, NamedInt): - return NamedInt( - '({0} + {1})'.format(self.__name__, other.__name__), - temp ) - else: - return temp - - class NEI(NamedInt, Enum): - __qualname__ = 'NEI' # needed for pickle protocol 4 - x = ('the-x', 1) - y = ('the-y', 2) - - - self.assertIs(NEI.__new__, Enum.__new__) - self.assertEqual(repr(NEI.x + NEI.y), "NamedInt('(the-x + the-y)', 3)") - globals()['NamedInt'] = NamedInt - globals()['NEI'] = NEI - NI5 = NamedInt('test', 5) - self.assertEqual(NI5, 5) - test_pickle_dump_load(self.assertEqual, NI5, 5, protocol=(4, HIGHEST_PROTOCOL)) - self.assertEqual(NEI.y.value, 2) - test_pickle_dump_load(self.assertTrue, NEI.y, protocol=(4, HIGHEST_PROTOCOL)) - - -class TestOrderV3(TestCase): - """ - Test definition order versus _order_ order. - """ - - def test_same_members(self): - class Color(Enum): - _order_ = 'red green blue' - red = 1 - green = 2 - blue = 3 - - def test_same_members_with_aliases(self): - class Color(Enum): - _order_ = 'red green blue' - red = 1 - green = 2 - blue = 3 - verde = green - - def test_same_members_wrong_order(self): - with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): - class Color(Enum): - _order_ = 'red green blue' - red = 1 - blue = 3 - green = 2 - - def test_order_has_extra_members(self): - with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): - class Color(Enum): - _order_ = 'red green blue purple' - red = 1 - green = 2 - blue = 3 - - def test_order_has_extra_members_with_aliases(self): - with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): - class Color(Enum): - _order_ = 'red green blue purple' - red = 1 - green = 2 - blue = 3 - verde = green - - def test_enum_has_extra_members(self): - with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): - class Color(Enum): - _order_ = 'red green blue' - red = 1 - green = 2 - blue = 3 - purple = 4 - - def test_enum_has_extra_members_with_aliases(self): - with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): - class Color(Enum): - _order_ = 'red green blue' - red = 1 - green = 2 - blue = 3 - purple = 4 - verde = green - - def test_same_members_flag(self): - class Color(Flag): - _order_ = 'red green blue' - red = 1 - green = 2 - blue = 4 - - def test_same_members_with_aliases_flag(self): - class Color(Flag): - _order_ = 'red green blue' - red = 1 - green = 2 - blue = 4 - verde = green - - def test_same_members_wrong_order_falg(self): - with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): - class Color(Flag): - _order_ = 'red green blue' - red = 1 - blue = 4 - green = 2 - - def test_order_has_extra_members_flag(self): - with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): - class Color(Flag): - _order_ = 'red green blue purple' - red = 1 - green = 2 - blue = 4 - - def test_order_has_extra_members_with_aliases_flag(self): - with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): - class Color(Flag): - _order_ = 'red green blue purple' - red = 1 - green = 2 - blue = 4 - verde = green - - def test_enum_has_extra_members_flag(self): - with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): - class Color(Flag): - _order_ = 'red green blue' - red = 1 - green = 2 - blue = 4 - purple = 8 - - def test_enum_has_extra_members_with_aliases_flag(self): - with self.assertRaisesRegex(TypeError, 'member order does not match _order_'): - class Color(Flag): - _order_ = 'red green blue' - red = 1 - green = 2 - blue = 4 - purple = 8 - verde = green - - -class TestNamedTupleV3(TestCase): - - def test_fixed_size(self): - class Book(NamedTuple, size=TupleSize.fixed): - title = 0 - author = 1 - genre = 2 - b = Book('Teckla', 'Steven Brust', 'fantasy') - self.assertTrue('Teckla' in b) - self.assertTrue('Steven Brust' in b) - self.assertTrue('fantasy' in b) - self.assertEqual(b.title, 'Teckla') - self.assertEqual(b.author, 'Steven Brust') - self.assertRaises(TypeError, Book, 'Teckla', 'Steven Brust') - self.assertRaises(TypeError, Book, 'Teckla') - - def test_minimum_size(self): - class Book(NamedTuple, size=TupleSize.minimum): - title = 0 - author = 1 - b = Book('Teckla', 'Steven Brust', 'fantasy') - self.assertTrue('Teckla' in b) - self.assertTrue('Steven Brust' in b) - self.assertTrue('fantasy' in b) - self.assertEqual(b.title, 'Teckla') - self.assertEqual(b.author, 'Steven Brust') - self.assertEqual(b[2], 'fantasy') - b = Book('Teckla', 'Steven Brust') - self.assertTrue('Teckla' in b) - self.assertTrue('Steven Brust' in b) - self.assertEqual(b.title, 'Teckla') - self.assertEqual(b.author, 'Steven Brust') - self.assertRaises(TypeError, Book, 'Teckla') - - def test_variable_size(self): - class Book(NamedTuple, size=TupleSize.variable): - title = 0 - author = 1 - genre = 2 - b = Book('Teckla', 'Steven Brust', 'fantasy') - self.assertTrue('Teckla' in b) - self.assertTrue('Steven Brust' in b) - self.assertTrue('fantasy' in b) - self.assertEqual(b.title, 'Teckla') - self.assertEqual(b.author, 'Steven Brust') - self.assertEqual(b.genre, 'fantasy') - b = Book('Teckla', 'Steven Brust') - self.assertTrue('Teckla' in b) - self.assertTrue('Steven Brust' in b) - self.assertEqual(b.title, 'Teckla') - self.assertEqual(b.author, 'Steven Brust') - self.assertRaises(AttributeError, getattr, b, 'genre') - self.assertRaises(TypeError, Book, title='Teckla', genre='fantasy') - self.assertRaises(TypeError, Book, author='Steven Brust') - - - -class TestStackoverflowAnswersV3(TestCase): - - def test_self_referential_directions(self): - # https://stackoverflow.com/a/64000706/208880 - class Directions(Enum): - # - NORTH = 1, 0 - WEST = 0, 1 - SOUTH = -1, 0 - EAST = 0, -1 - # - def __init__(self, x, y): - self.x = x - self.y = y - if len(self.__class__): - # make links - all = list(self.__class__) - left, right = all[0], all[-1] - self.left = left - self.right = right - left.right = self - right.left = self - # - D = Directions - self.assertEqual(D.NORTH.value, (1, 0)) - self.assertTrue(D.NORTH.left is D.WEST) - self.assertTrue(D.SOUTH.right is D.WEST) - - def test_self_referential_rock_paper_scissors(self): - # https://stackoverflow.com/a/57085357/208880 - class RPS(Enum): - # - Rock = "rock" - Paper = "paper" - Scissors = "scissors" - # - def __init__(self, value): - if len(self.__class__): - # make links - all = list(self.__class__) - first, previous = all[0], all[-1] - first.beats = self - self.beats = previous - # - self.assertTrue(RPS.Rock.beats is RPS.Scissors) - self.assertTrue(RPS.Scissors.beats is RPS.Paper) - self.assertTrue(RPS.Paper.beats is RPS.Rock) - - def test_arduino_headers(self): - # https://stackoverflow.com/q/65048495/208880 - class CHeader(Enum): - def __init_subclass__(cls, **kwds): - # write Enums to C header file - cls_name = cls.__name__ - header_path = getattr(cls, '_%s__header' % cls_name) - with open(header_path, 'w') as fh: - fh.write('initial header stuff here\n') - for enum in cls: - fh.write('#define %s %r\n' % (enum.name, enum.value)) - class Arduino(CHeader): - __header = os.path.join(tempdir, 'arduino.h') - ONE = 1 - TWO = 2 - with open(os.path.join(tempdir, 'arduino.h')) as fh: - data = fh.read() - self.assertEqual(textwrap.dedent("""\ - initial header stuff here - #define ONE 1 - #define TWO 2 - """), - data, - ) - - def test_create_C_like_Enum(self): - # https://stackoverflow.com/a/35965438/208880 - class Id(Enum, settings=MagicValue, start=0): - # - NONE # 0x0 - HEARTBEAT # 0x1 - FLUID_TRANSFER_REQUEST - FLUID_TRANSFER_STATUS_MSG - FLUID_TRANSFER_ERROR_MSG - # ... - # - # Camera App Messages - START_SENDING_PICTURES = 0x010000 - STOP_SENDING_PICTURES - START_RECORDING_VIDEO_REQ - STOP_RECORDING_VIDEO_REQ - # ... - # - # Sensor Calibration - VOLUME_REQUEST = 0x020000 - START_CAL - CLI_COMMAND_REQUEST - CLI_COMMAND_RESPONSE - # - # File Mananger - NEW_DELIVERY_REQ = 0x30000 - GET_DELIVERY_FILE_REQ - GET_FILE_REQ - # - ACK_NACK - RESPONSE - # - LAST_ID - # - self.assertEqual(Id.NONE.value, 0) - self.assertEqual(Id.FLUID_TRANSFER_ERROR_MSG.value, 4) - self.assertEqual(Id.START_SENDING_PICTURES.value, 0x010000) - self.assertEqual(Id.STOP_RECORDING_VIDEO_REQ.value, 0x010003) - self.assertEqual(Id.START_CAL.value, 0x020001) - self.assertEqual(Id.LAST_ID.value, 0x30005) - - - @unittest.skipUnless(pyparsing, 'pyparsing not installed') - def test_c_header_scanner(self): - # https://stackoverflow.com/questions/58732872/208880 - with open(os.path.join(tempdir, 'c_plus_plus.h'), 'w') as fh: - fh.write(""" - stuff before - enum hello { - Zero, - One, - Two, - Three, - Five=5, - Six, - Ten=10 - }; - in the middle - enum blah - { - alpha, - beta, - gamma = 10 , - zeta = 50 - }; - at the end - """) - from pyparsing import Group, Optional, Suppress, Word, ZeroOrMore - from pyparsing import alphas, alphanums, nums - # - CPPEnum = None - class CPPEnumType(EnumMeta): - # - @classmethod - def __prepare__(metacls, clsname, bases, **kwds): - # return a standard dictionary for the initial processing - return {} - # - def __init__(clsname, *args , **kwds): - super(CPPEnumType, clsname).__init__(*args) - # - def __new__(metacls, clsname, bases, clsdict, **kwds): - if CPPEnum is None: - # first time through, ignore the rest - enum_dict = super(CPPEnumType, metacls).__prepare__(clsname, bases, **kwds) - enum_dict.update(clsdict) - return super(CPPEnumType, metacls).__new__(metacls, clsname, bases, enum_dict, **kwds) - members = [] - # - # remove _file and _name using `pop()` as they will cause problems in EnumMeta - try: - file = clsdict.pop('_file') - except KeyError: - raise TypeError('_file not specified') - cpp_enum_name = clsdict.pop('_name', clsname.lower()) - with open(file) as fh: - file_contents = fh.read() - # - # syntax we don't want to see in the final parse tree - LBRACE, RBRACE, EQ, COMMA = map(Suppress, "{}=,") - _enum = Suppress("enum") - identifier = Word(alphas, alphanums + "_") - integer = Word(nums) - enumValue = Group(identifier("name") + Optional(EQ + integer("value"))) - enumList = Group(enumValue + ZeroOrMore(COMMA + enumValue)) - enum = _enum + identifier("enum") + LBRACE + enumList("names") + RBRACE - # - # find the cpp_enum_name ignoring other syntax and other enums - for item, start, stop in enum.scanString(file_contents): - if item.enum != cpp_enum_name: - continue - id = 0 - for entry in item.names: - if entry.value != "": - id = int(entry.value) - members.append((entry.name.upper(), id)) - id += 1 - # - # get the real EnumDict - enum_dict = super(CPPEnumType, metacls).__prepare__(clsname, bases, **kwds) - # transfer the original dict content, names starting with '_' first - items = list(clsdict.items()) - items.sort(key=lambda p: (0 if p[0][0] == '_' else 1, p)) - for name, value in items: - enum_dict[name] = value - # add the members - for name, value in members: - enum_dict[name] = value - return super(CPPEnumType, metacls).__new__(metacls, clsname, bases, enum_dict, **kwds) - # - class CPPEnum(IntEnum, metaclass=CPPEnumType): - pass - # - class Hello(CPPEnum): - _file = os.path.join(tempdir, 'c_plus_plus.h') - # - class Blah(CPPEnum): - _file = os.path.join(tempdir, 'c_plus_plus.h') - _name = 'blah' - # - self.assertEqual( - list(Hello), - [Hello.ZERO, Hello.ONE, Hello.TWO, Hello.THREE, Hello.FIVE, Hello.SIX, Hello.TEN], - ) - self.assertEqual(Hello.ZERO.value, 0) - self.assertEqual(Hello.THREE.value, 3) - self.assertEqual(Hello.SIX.value, 6) - self.assertEqual(Hello.TEN.value, 10) - # - self.assertEqual( - list(Blah), - [Blah.ALPHA, Blah.BETA, Blah.GAMMA, Blah.ZETA], - ) - self.assertEqual(Blah.ALPHA.value, 0) - self.assertEqual(Blah.BETA.value, 1) - self.assertEqual(Blah.GAMMA.value, 10) - self.assertEqual(Blah.ZETA.value, 50) - -class TestIssuesV3(TestCase): - """ - Problems that were stated in issues. - """ - - def test_auto_multi_int_1(self): - class Measurement(int, AddValueEnum, MultiValueEnum, start=0): - one = "20110721" - two = "20120911" - three = "20110518" - self.assertEqual([m.value for m in Measurement], [0, 1, 2]) - self.assertEqual([m.name for m in Measurement], ['one', 'two', 'three']) - self.assertIs(Measurement(0), Measurement.one) - self.assertIs(Measurement('20110721'), Measurement.one) - self.assertIs(Measurement(1), Measurement.two) - self.assertIs(Measurement('20120911'), Measurement.two) - self.assertIs(Measurement(2), Measurement.three) - self.assertIs(Measurement('20110518'), Measurement.three) - - def test_auto_multi_int_2(self): - class Measurement(int, Enum, settings=(MultiValue, AddValue), start=0): - one = "20110721" - two = "20120911" - three = "20110518" - self.assertEqual([m.value for m in Measurement], [0, 1, 2]) - self.assertEqual([m.name for m in Measurement], ['one', 'two', 'three']) - self.assertIs(Measurement(0), Measurement.one) - self.assertIs(Measurement('20110721'), Measurement.one) - self.assertIs(Measurement(1), Measurement.two) - self.assertIs(Measurement('20120911'), Measurement.two) - self.assertIs(Measurement(2), Measurement.three) - self.assertIs(Measurement('20110518'), Measurement.three) - - def test_extend_enum_with_init(self): - class Color(Enum, settings=MultiValue, init='foo bar'): - red = '1', 'yes' - green = '2', 'no' - blue = '3', 'maybe' - self.assertEqual(Color.red.value, '1') - self.assertEqual(Color.red.foo, '1') - self.assertEqual(Color.red.bar, 'yes') - extend_enum(Color, 'opacity', '4', 'never') - self.assertEqual(list(Color), [Color.red, Color.green, Color.blue, Color.opacity]) - self.assertEqual(Color.opacity.value, '4') - self.assertEqual(Color.opacity.name, 'opacity') - self.assertTrue(Color('4') is Color.opacity) - self.assertTrue(Color('never') is Color.opacity) - -class TestExtendEnumV3(TestCase): - - def test_extend_enum_plain(self): - class Color(Enum): - red = 1 - green = 2 - blue = 3 - self.assertRaisesRegex(TypeError, '.blue. already in use as property..Color.blue: 3.', extend_enum, Color, 'blue', 5) - # - extend_enum(Color, 'brown', 4) - self.assertEqual(Color.brown.name, 'brown') - self.assertEqual(Color.brown.value, 4) - self.assertTrue(Color.brown in Color) - self.assertEqual(Color(4), Color.brown) - self.assertEqual(Color['brown'], Color.brown) - self.assertEqual(len(Color), 4) - # - extend_enum(Color, 'mauve') - self.assertEqual(Color.mauve.name, 'mauve') - self.assertEqual(Color.mauve.value, 5) - self.assertTrue(Color.mauve in Color) - self.assertEqual(Color(5), Color.mauve) - self.assertEqual(Color['mauve'], Color.mauve) - self.assertEqual(len(Color), 5) - - def test_extend_enum_alias(self): - class Color(Enum): - red = 1 - green = 2 - blue = 3 - extend_enum(Color, 'rojo', 1) - self.assertEqual(Color.rojo.name, 'red') - self.assertEqual(Color.rojo.value, 1) - self.assertTrue(Color.rojo in Color) - self.assertEqual(Color(1), Color.rojo) - self.assertEqual(Color['rojo'], Color.red) - self.assertEqual(len(Color), 3) - - def test_extend_enum_unique(self): - class Color(UniqueEnum): - red = 1 - green = 2 - blue = 3 - self.assertRaisesRegex(ValueError, r' is a duplicate of ', extend_enum, Color, 'rojo', 1) - # - self.assertEqual(Color.red.name, 'red') - self.assertEqual(Color.red.value, 1) - self.assertTrue(Color.red in Color) - self.assertEqual(Color(1), Color.red) - self.assertEqual(Color['red'], Color.red) - self.assertEqual(Color.green.name, 'green') - self.assertEqual(Color.green.value, 2) - self.assertTrue(Color.green in Color) - self.assertEqual(Color(2), Color.green) - self.assertEqual(Color['blue'], Color.blue) - self.assertEqual(Color.blue.name, 'blue') - self.assertEqual(Color.blue.value, 3) - self.assertTrue(Color.blue in Color) - self.assertEqual(Color(3), Color.blue) - self.assertEqual(len(Color), 3) - # - extend_enum(Color, 'brown', 4) - self.assertEqual(Color.brown.name, 'brown') - self.assertEqual(Color.brown.value, 4) - self.assertTrue(Color.brown in Color) - self.assertEqual(Color(4), Color.brown) - self.assertEqual(Color['brown'], Color.brown) - self.assertEqual(len(Color), 4) - # - self.assertRaisesRegex(ValueError, ' is a duplicate of ', extend_enum, Color, 'verde', 2) - # - extend_enum(Color, 'mauve') - self.assertEqual(Color.mauve.name, 'mauve') - self.assertEqual(Color.mauve.value, 5) - self.assertTrue(Color.mauve in Color) - self.assertEqual(Color(5), Color.mauve) - self.assertEqual(Color['mauve'], Color.mauve) - self.assertEqual(len(Color), 5) - - - def test_extend_enum_shadow_property(self): - class Color(Enum): - red = 1 - green = 2 - blue = 3 - extend_enum(Color, 'value', 4) - self.assertEqual(Color.value.name, 'value') - self.assertEqual(Color.value.value, 4) - self.assertTrue(Color.value in Color) - self.assertEqual(Color(4), Color.value) - self.assertEqual(Color['value'], Color.value) - self.assertEqual(len(Color), 4) - self.assertEqual(Color.red.value, 1) - - def test_extend_enum_shadow_base(self): - class hohum(object): - def cyan(self): - "cyanize a color" - return self.value - class Color(hohum, Enum): - red = 1 - green = 2 - blue = 3 - self.assertRaisesRegex(TypeError, r'already in use in superclass', extend_enum, Color, 'cyan', 4) - self.assertEqual(len(Color), 3) - self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) - - def test_extend_enum_multivalue(self): - class Color(MultiValueEnum): - red = 1, 4, 7 - green = 2, 5, 8 - blue = 3, 6, 9 - extend_enum(Color, 'brown', 10, 20) - self.assertEqual(Color.brown.name, 'brown') - self.assertEqual(Color.brown.value, 10) - self.assertTrue(Color.brown in Color) - self.assertEqual(Color(10), Color.brown) - self.assertEqual(Color(20), Color.brown) - self.assertEqual(Color['brown'], Color.brown) - self.assertEqual(len(Color), 4) - # - self.assertRaisesRegex(ValueError, 'no values specified for MultiValue enum', extend_enum, Color, 'mauve') - - def test_extend_enum_multivalue_alias(self): - class Color(MultiValueEnum): - red = 1, 4, 7 - green = 2, 5, 8 - blue = 3, 6, 9 - self.assertRaisesRegex(ValueError, r' is a duplicate of ', extend_enum, Color, 'rojo', 7) - self.assertEqual(Color.red.name, 'red') - self.assertEqual(Color.red.value, 1) - self.assertTrue(Color.red in Color) - self.assertEqual(Color(1), Color.red) - self.assertEqual(Color(4), Color.red) - self.assertEqual(Color(7), Color.red) - self.assertEqual(Color['red'], Color.red) - self.assertEqual(Color.green.name, 'green') - self.assertEqual(Color.green.value, 2) - self.assertTrue(Color.green in Color) - self.assertEqual(Color(2), Color.green) - self.assertEqual(Color(5), Color.green) - self.assertEqual(Color(8), Color.green) - self.assertEqual(Color['blue'], Color.blue) - self.assertEqual(Color.blue.name, 'blue') - self.assertEqual(Color.blue.value, 3) - self.assertTrue(Color.blue in Color) - self.assertEqual(Color(3), Color.blue) - self.assertEqual(Color(6), Color.blue) - self.assertEqual(Color(9), Color.blue) - self.assertEqual(len(Color), 3) - - def test_extend_enum_multivalue_str(self): - class M(str, MultiValueEnum): - VALUE_1 = 'value_1', 'VALUE_1' - VALUE_2 = 'value_2', 'VALUE_2' - VALUE_3 = 'value_3', 'VALUE_3' - self.assertTrue(M._member_type_ is str) - extend_enum(M, 'VALUE_4', 'value_4', 'VALUE_4') - self.assertEqual(list(M), [M.VALUE_1, M.VALUE_2, M.VALUE_3, M.VALUE_4]) - self.assertTrue(M('value_4') is M.VALUE_4) - self.assertTrue(M('VALUE_4') is M.VALUE_4) - self.assertTrue(M.VALUE_4.name == 'VALUE_4') - self.assertTrue(M.VALUE_4.value == 'value_4') - - def test_extend_intenum(self): - class Index(IntEnum): - DeviceType = 0x1000 - ErrorRegister = 0x1001 - - for name, value in ( - ('ControlWord', 0x6040), - ('StatusWord', 0x6041), - ('OperationMode', 0x6060), - ): - extend_enum(Index, name, value) - - self.assertEqual(len(Index), 5) - self.assertEqual(list(Index), [Index.DeviceType, Index.ErrorRegister, Index.ControlWord, Index.StatusWord, Index.OperationMode]) - self.assertEqual(Index.DeviceType.value, 0x1000) - self.assertEqual(Index.StatusWord.value, 0x6041) - - def test_extend_multi_init(self): - class HTTPStatus(IntEnum): - def __new__(cls, value, phrase, description): - obj = int.__new__(cls, value) - obj._value_ = value - - obj.phrase = phrase - obj.description = description - return obj - CONTINUE = 100, 'Continue', 'Request received, please continue' - SWITCHING_PROTOCOLS = 101, 'Switching Protocols', 'Switching to new protocol; obey Upgrade header' - PROCESSING = 102, 'Processing', '' - length = 3 - extend_enum(HTTPStatus, 'BAD_SPAM', 513, 'Too greasy', 'for a train') - extend_enum(HTTPStatus, 'BAD_EGGS', 514, 'Too green', '') - self.assertEqual(len(HTTPStatus), length+2) - self.assertEqual( - list(HTTPStatus)[-2:], - [HTTPStatus.BAD_SPAM, HTTPStatus.BAD_EGGS], - ) - self.assertEqual(HTTPStatus.BAD_SPAM.value, 513) - self.assertEqual(HTTPStatus.BAD_SPAM.name, 'BAD_SPAM') - self.assertEqual(HTTPStatus.BAD_SPAM.phrase, 'Too greasy') - self.assertEqual(HTTPStatus.BAD_SPAM.description, 'for a train') - self.assertEqual(HTTPStatus.BAD_EGGS.value, 514) - self.assertEqual(HTTPStatus.BAD_EGGS.name, 'BAD_EGGS') - self.assertEqual(HTTPStatus.BAD_EGGS.phrase, 'Too green') - self.assertEqual(HTTPStatus.BAD_EGGS.description, '') - - def test_extend_flag(self): - class Color(Flag): - BLACK = 0 - RED = 1 - GREEN = 2 - BLUE = 4 - extend_enum(Color, 'MAGENTA') - self.assertTrue(Color(8) is Color.MAGENTA) - self.assertTrue(isinstance(Color.MAGENTA, Color)) - self.assertEqual(Color.MAGENTA.value, 8) - extend_enum(Color, 'PURPLE', 11) - self.assertTrue(Color(11) is Color.PURPLE) - self.assertTrue(isinstance(Color.PURPLE, Color)) - self.assertEqual(Color.PURPLE.value, 11) - self.assertTrue(issubclass(Color, Flag)) - - def test_extend_flag_backwards(self): - class Color(Flag): - BLACK = 0 - RED = 1 - GREEN = 2 - BLUE = 4 - extend_enum(Color, 'PURPLE', 11) - self.assertTrue(Color(11) is Color.PURPLE) - self.assertTrue(isinstance(Color.PURPLE, Color)) - self.assertEqual(Color.PURPLE.value, 11) - self.assertTrue(issubclass(Color, Flag)) - # - extend_enum(Color, 'MAGENTA') - self.assertTrue(Color(8) is Color.MAGENTA) - self.assertTrue(isinstance(Color.MAGENTA, Color)) - self.assertEqual(Color.MAGENTA.value, 8) - # - extend_enum(Color, 'mauve') - self.assertEqual(Color.mauve.name, 'mauve') - self.assertEqual(Color.mauve.value, 16) - self.assertTrue(Color.mauve in Color) - self.assertEqual(Color(16), Color.mauve) - self.assertEqual(Color['mauve'], Color.mauve) - self.assertEqual(len(Color), 5) - - def test_extend_intflag(self): - class Color(IntFlag): - BLACK = 0 - RED = 1 - GREEN = 2 - BLUE = 4 - extend_enum(Color, 'MAGENTA') - self.assertTrue(Color(8) is Color.MAGENTA) - self.assertTrue(isinstance(Color.MAGENTA, Color)) - self.assertEqual(Color.MAGENTA.value, 8) - extend_enum(Color, 'PURPLE', 11) - self.assertTrue(Color(11) is Color.PURPLE) - self.assertTrue(isinstance(Color.PURPLE, Color)) - self.assertEqual(Color.PURPLE.value, 11) - self.assertTrue(issubclass(Color, Flag)) - # - extend_enum(Color, 'mauve') - self.assertEqual(Color.mauve.name, 'mauve') - self.assertEqual(Color.mauve.value, 16) - self.assertTrue(Color.mauve in Color) - self.assertEqual(Color(16), Color.mauve) - self.assertEqual(Color['mauve'], Color.mauve) - self.assertEqual(len(Color), 5) - - def test_extend_intflag_backwards(self): - class Color(IntFlag): - BLACK = 0 - RED = 1 - GREEN = 2 - BLUE = 4 - extend_enum(Color, 'PURPLE', 11) - self.assertTrue(Color(11) is Color.PURPLE) - self.assertTrue(isinstance(Color.PURPLE, Color)) - self.assertEqual(Color.PURPLE.value, 11) - self.assertTrue(issubclass(Color, Flag)) - # - extend_enum(Color, 'MAGENTA') - self.assertTrue(Color(8) is Color.MAGENTA) - self.assertTrue(isinstance(Color.MAGENTA, Color)) - self.assertEqual(Color.MAGENTA.value, 8) - # - extend_enum(Color, 'mauve') - self.assertEqual(Color.mauve.name, 'mauve') - self.assertEqual(Color.mauve.value, 16) - self.assertTrue(Color.mauve in Color) - self.assertEqual(Color(16), Color.mauve) - self.assertEqual(Color['mauve'], Color.mauve) - self.assertEqual(len(Color), 5) - - def test_extend_strenum(self): - class Color(StrEnum): - RED = auto() - GREEN = auto() - BLUE = auto() - extend_enum(Color, 'BLACK') - self.assertEqual(Color.BLACK.name, 'BLACK') - self.assertEqual(Color.BLACK.value, 'black') - self.assertEqual(len(Color), 4) - - @unittest.skipUnless(StdlibEnum, 'Stdlib Enum not available') - def test_extend_enum_stdlib(self): - class Color(StdlibEnum): - red = 1 - green = 2 - blue = 3 - self.assertEqual(getattr(Color.red, '_values_', None), None) - extend_enum(Color, 'brown', 4) - self.assertEqual(Color.brown.name, 'brown') - self.assertEqual(Color.brown.value, 4) - self.assertTrue(Color.brown in Color) - self.assertEqual(Color(4), Color.brown) - self.assertEqual(Color['brown'], Color.brown) - self.assertEqual(len(Color), 4) - - @unittest.skipUnless(StdlibEnum, 'Stdlib Enum not available') - def test_extend_enum_plain_stdlib(self): - class Color(StdlibEnum): - red = 1 - green = 2 - blue = 3 - self.assertRaisesRegex(TypeError, 'already in use as', extend_enum, Color, 'blue', 5) - # - extend_enum(Color, 'brown', 4) - self.assertEqual(Color.brown.name, 'brown') - self.assertEqual(Color.brown.value, 4) - self.assertTrue(Color.brown in Color) - self.assertEqual(Color(4), Color.brown) - self.assertEqual(Color['brown'], Color.brown) - self.assertEqual(len(Color), 4) - self.assertEqual(list(Color), [Color.red, Color.green, Color.blue, Color.brown]) - self.assertEqual([c.value for c in Color], [1, 2, 3, 4]) - # - extend_enum(Color, 'mauve') - self.assertEqual(Color.mauve.name, 'mauve') - self.assertEqual(Color.mauve.value, 5) - self.assertTrue(Color.mauve in Color) - self.assertEqual(Color(5), Color.mauve) - self.assertEqual(Color['mauve'], Color.mauve) - self.assertEqual(len(Color), 5) - - @unittest.skipUnless(StdlibEnum, 'Stdlib Enum not available') - def test_extend_enum_alias_stdlib(self): - class Color(StdlibEnum): - red = 1 - green = 2 - blue = 3 - extend_enum(Color, 'rojo', 1) - self.assertEqual(Color.rojo.name, 'red') - self.assertEqual(Color.rojo.value, 1) - self.assertTrue(Color.rojo in Color) - self.assertEqual(Color(1), Color.rojo) - self.assertEqual(Color['rojo'], Color.red) - self.assertEqual(len(Color), 3) - - @unittest.skipUnless(StdlibEnum, 'Stdlib Enum not available') - def test_extend_enum_shadow_property_stdlib(self): - class Color(StdlibEnum): - red = 1 - green = 2 - blue = 3 - extend_enum(Color, 'value', 4) - self.assertEqual(Color.value.name, 'value') - self.assertEqual(Color.value.value, 4) - self.assertTrue(Color.value in Color) - self.assertEqual(Color(4), Color.value) - self.assertEqual(Color['value'], Color.value) - self.assertEqual(len(Color), 4) - self.assertEqual(Color.red.value, 1) - - @unittest.skipUnless(StdlibEnum, 'Stdlib Enum not available') - def test_extend_enum_shadow_base_stdlib(self): - class hohum(object): - def cyan(self): - "cyanize a color" - return self.value - class Color(hohum, StdlibEnum): - red = 1 - green = 2 - blue = 3 - self.assertRaisesRegex(TypeError, r'already in use in superclass', extend_enum, Color, 'cyan', 4) - self.assertEqual(len(Color), 3) - self.assertEqual(list(Color), [Color.red, Color.green, Color.blue]) - - @unittest.skipUnless(StdlibIntEnum, 'Stdlib IntEnum not available') - def test_extend_intenum_stdlib(self): - class Index(StdlibIntEnum): - DeviceType = 0x1000 - ErrorRegister = 0x1001 - - for name, value in ( - ('ControlWord', 0x6040), - ('StatusWord', 0x6041), - ('OperationMode', 0x6060), - ): - extend_enum(Index, name, value) - - self.assertEqual(len(Index), 5) - self.assertEqual(list(Index), [Index.DeviceType, Index.ErrorRegister, Index.ControlWord, Index.StatusWord, Index.OperationMode]) - self.assertEqual(Index.DeviceType.value, 0x1000) - self.assertEqual(Index.StatusWord.value, 0x6041) - - @unittest.skipUnless(StdlibIntEnum, 'Stdlib IntEnum not available') - def test_extend_multi_init_stdlib(self): - class HTTPStatus(StdlibIntEnum): - def __new__(cls, value, phrase, description): - obj = int.__new__(cls, value) - obj._value_ = value - - obj.phrase = phrase - obj.description = description - return obj - CONTINUE = 100, 'Continue', 'Request received, please continue' - SWITCHING_PROTOCOLS = 101, 'Switching Protocols', 'Switching to new protocol; obey Upgrade header' - PROCESSING = 102, 'Processing', '' - length = 3 - extend_enum(HTTPStatus, 'BAD_SPAM', 513, 'Too greasy', 'for a train') - extend_enum(HTTPStatus, 'BAD_EGGS', 514, 'Too green', '') - self.assertEqual(len(HTTPStatus), length+2) - self.assertEqual( - list(HTTPStatus)[-2:], - [HTTPStatus.BAD_SPAM, HTTPStatus.BAD_EGGS], - ) - self.assertEqual(HTTPStatus.BAD_SPAM.value, 513) - self.assertEqual(HTTPStatus.BAD_SPAM.name, 'BAD_SPAM') - self.assertEqual(HTTPStatus.BAD_SPAM.phrase, 'Too greasy') - self.assertEqual(HTTPStatus.BAD_SPAM.description, 'for a train') - self.assertEqual(HTTPStatus.BAD_EGGS.value, 514) - self.assertEqual(HTTPStatus.BAD_EGGS.name, 'BAD_EGGS') - self.assertEqual(HTTPStatus.BAD_EGGS.phrase, 'Too green') - self.assertEqual(HTTPStatus.BAD_EGGS.description, '') - - @unittest.skipUnless(StdlibFlag, 'Stdlib Flag not available') - def test_extend_flag_stdlib(self): - class Color(StdlibFlag): - BLACK = 0 - RED = 1 - GREEN = 2 - BLUE = 4 - extend_enum(Color, 'MAGENTA') - self.assertTrue(Color(8) is Color.MAGENTA) - self.assertTrue(isinstance(Color.MAGENTA, Color)) - self.assertEqual(Color.MAGENTA.value, 8) - extend_enum(Color, 'PURPLE', 11) - self.assertTrue(Color(11) is Color.PURPLE) - self.assertTrue(isinstance(Color.PURPLE, Color)) - self.assertEqual(Color.PURPLE.value, 11) - self.assertTrue(issubclass(Color, StdlibFlag)) - - @unittest.skipUnless(StdlibFlag, 'Stdlib Flag not available') - def test_extend_flag_backwards_stdlib(self): - class Color(StdlibFlag): - BLACK = 0 - RED = 1 - GREEN = 2 - BLUE = 4 - extend_enum(Color, 'PURPLE', 11) - self.assertTrue(Color(11) is Color.PURPLE) - self.assertTrue(isinstance(Color.PURPLE, Color)) - self.assertEqual(Color.PURPLE.value, 11) - self.assertTrue(issubclass(Color, StdlibFlag)) - # - extend_enum(Color, 'MAGENTA') - self.assertTrue(Color(16) is Color.MAGENTA) - self.assertTrue(isinstance(Color.MAGENTA, Color)) - self.assertEqual(Color.MAGENTA.value,16) - # - extend_enum(Color, 'mauve') - self.assertEqual(Color.mauve.name, 'mauve') - self.assertEqual(Color.mauve.value, 32) - self.assertTrue(Color.mauve in Color) - self.assertEqual(Color(32), Color.mauve) - self.assertEqual(Color['mauve'], Color.mauve) - - @unittest.skipUnless(StdlibIntFlag, 'Stdlib IntFlag not available') - def test_extend_intflag_stdlib(self): - class Color(StdlibIntFlag): - BLACK = 0 - RED = 1 - GREEN = 2 - BLUE = 4 - extend_enum(Color, 'MAGENTA') - self.assertTrue(Color(8) is Color.MAGENTA) - self.assertTrue(isinstance(Color.MAGENTA, Color)) - self.assertEqual(Color.MAGENTA.value, 8) - extend_enum(Color, 'PURPLE', 11) - self.assertTrue(Color(11) is Color.PURPLE) - self.assertTrue(isinstance(Color.PURPLE, Color)) - self.assertEqual(Color.PURPLE.value, 11) - self.assertTrue(issubclass(Color, StdlibFlag)) - # - extend_enum(Color, 'mauve') - self.assertEqual(Color.mauve.name, 'mauve') - self.assertEqual(Color.mauve.value, 16) - self.assertTrue(Color.mauve in Color) - self.assertEqual(Color(16), Color.mauve) - self.assertEqual(Color['mauve'], Color.mauve) - - @unittest.skipUnless(StdlibIntFlag, 'Stdlib IntFlag not available') - def test_extend_intflag_backwards_stdlib(self): - class Color(StdlibIntFlag): - BLACK = 0 - RED = 1 - GREEN = 2 - BLUE = 4 - if pyver >= PY3_11: - # flags make more sense in 3.11 - length = 5 - MAGENTA = 8 - mauve = 16 - else: - length = 7 - MAGENTA = 16 - mauve = 32 - extend_enum(Color, 'PURPLE', 11) - self.assertTrue(Color(11) is Color.PURPLE) - self.assertTrue(isinstance(Color.PURPLE, Color)) - self.assertEqual(Color.PURPLE.value, 11) - self.assertTrue(issubclass(Color, StdlibFlag)) - # - extend_enum(Color, 'MAGENTA') - self.assertTrue(Color(MAGENTA) is Color.MAGENTA) - self.assertTrue(isinstance(Color.MAGENTA, Color)) - self.assertEqual(Color.MAGENTA.value, MAGENTA) - # - extend_enum(Color, 'mauve') - self.assertEqual(Color.mauve.name, 'mauve') - self.assertEqual(Color.mauve.value, mauve) - self.assertTrue(Color.mauve in Color) - self.assertEqual(Color(mauve), Color.mauve) - self.assertEqual(Color['mauve'], Color.mauve) - self.assertEqual(len(Color), length, list(Color)) - - @unittest.skipUnless(StdlibStrEnum, 'Stdlib StrEnum not available') - def test_extend_strenum_stdlib(self): - class Color(StrEnum): - RED = auto() - GREEN = auto() - BLUE = auto() - extend_enum(Color, 'BLACK') - self.assertEqual(Color.BLACK.name, 'BLACK') - self.assertEqual(Color.BLACK.value, 'black') - self.assertEqual(len(Color), 4) - - -if __name__ == '__main__': - raise RuntimeError("'test_v3.py' should not be run by itself; it's included in 'test.py'") diff --git a/venv/Lib/site-packages/blendmodes/__init__.py b/venv/Lib/site-packages/blendmodes/__init__.py deleted file mode 100644 index 2b6e91ad..00000000 --- a/venv/Lib/site-packages/blendmodes/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -"""Use this module to apply a number of blending modes to a background and foreground image -""" diff --git a/venv/Lib/site-packages/blendmodes/__pycache__/__init__.cpython-39.pyc b/venv/Lib/site-packages/blendmodes/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index da1d6a38859540c7045ecb683d0533d09663f8a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 285 zcmYjMK~BUl3{3aJYNh>yuX`vDKnU@Iy#NPLP~0LCRtQI!8`a1FW4(jKwLQC z77kb%+ap*%tC#z`M34`hUav|*$_$^&+^+3%{;nK3Rj#=NOU|ekNO3AITqe&sHbSR7cU3|4A>V7DL(? U_MDA5kJD*=`J3cwj{GT5KLp5FlmGw# diff --git a/venv/Lib/site-packages/blendmodes/__pycache__/blend.cpython-39.pyc b/venv/Lib/site-packages/blendmodes/__pycache__/blend.cpython-39.pyc deleted file mode 100644 index 231846003cc72829e6134e11092b415beb764453..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13302 zcmcIqTWlOhcJ1!zdGaBOqTaIHl4Xk{OGEjwe$evD6dw|8isX{i!y{?w=2Vj$ab|j4 z-9w4Qa1lV>0Gq(Df0B>DNGxm;L_iS0!D9asK1(?CHrU`25%3e`YH0D$2joL-dox!+VONI^SrDB9y!$ zRH2pBvX<9Ws>e#Pe2jnfd>p@eDN#=5lW2>JL@8BH=hLVs`J4J8CekAFiJs3yAYGyx zkgf=%NAv>H9f9l-eSq{tAbUkWAiWXDJ~05uo(N>WH~>gr1aeRu0%UIla#$Pzq(1^V zDvklNF9LZ^90z0|0(o8-fb5SzPKc9$9Ed<(5T^h+7=gSfUIOG$1aevo0&+M4$%-?8 z9Em_)7H0uD8iAY>IY5p@AVcCjAkRf0uZULxIUa$$CSC{Rd4_!N3r)Ns-o&%v_jn6? z^R^fU^3Au6&`3N%1~lgDtQR#1vrJ7T8rW4cM6$*o>G3>}7`i0Bf5Q^LRcRJTHiAcs|F^ z*HPZUd^g31faQcKZsB`d+(G%nFXCcReDp~?KO|!FN_NS73v53t?e(ItjHQxQ5yi@i zv0SSZ+@f7^46`B(ccW@KxlCq6m{r#j#^Tgnye@{lP+(XlJz zrBcy$-J$A6RaAzSO7_xF*>qe>`VeWE!jie_MurBDj8ihbaf+2SRLyD?FWAx?I@dM` z!7dT1({^Rvs?tbTCl`#VV!^67mN9BqH)L^T)inkSS>x67ubww1q$RFkz>Q_IU}Z9+ zcr3b(;o8HQU0;3WZDZD~m5leTdseBGnXsi%ELTfb*{Zlir;uPo5_i=yW~x@jnB(Od zlVx+oGUlsR;R&<6WsG>FGkn@TX{K4TSaEaH;|ot1_)Vi~I;&=7#oj)mX;fqG$W-o;@<@}^N0M@v>1Oe({KD|#N;VlU5$;_`CM0pp*$XD!u=x7V%8`t4G2>9$jJt#ehNi{(4FdE3hv$ghF}8;Oc7 z%Vy~~CUbNYil(Wu8^7C@5eJZ$(T|F&e5uyex-!6a=OM{tY;#|@q^sAa7A9w>KFs|G zI`hY`d~@-U*3c@|hF&NYtCEadxKi1L$407LE4jsLY2y^09HyowY0_;d5yFg8A1aIz zsCy77J=B1Z7DZ@eY;1C2^7?pA_M$HcO|LYPKDjDNX)z98g2`jA*bG#NqEhcoK*Of!S@+e9Xd#K?*c0i2- zUU@}vuAB$k(Dy_9i#S%3*xVnIcXVcI=Gxq)Yje}NMxwZaJ+vOFjf5zYK9oJcf|og+ z)xCA5EGfY&uGNU&gIrixxOsoag=Y$O$*##IFmnz=IVlv1)~o93@yGHQ>N^u)4y_Ra zyxLBHIu>154=G1m7q7Q0eynBjIu;+_JkY-Qv6->U*v$i&P#&a$_I3J^w_tggAV;Ve zprR9-b$P24wpg*=KqsI5dsRuou=5h1w8vPYzOVB#pS;KY;WFpOC#J?n7uuKjH3`JD zd~T;@C8f1ovI_16T2jl-8GtceBOPP~=0*DYBxH>L<@cH}i(O{f|5z_;>+PFM$;jJUX|$$*t- zDymj7k5Pb!Ly6ZM@D0tw8a2q<|HcD*;r9Y3;`n=v4m5QmQ^W6<~-AB&OjjEM=MeFq4HVI)63Xh6}6ZPqx3*UsXkCgP-BCn zwdmvvEjpRZYizSOljSvHU<2NfpJMlIS)oOftE9v-kSci|wQaG7Kjb0cKw7~Q~ok$VXHPORU(B5{(i?z_?~xW7%v`Ks;pr$&mrL?9L-716Pb)Yb7h z@-Lnir3|A~w&aTSyAg!Gw<|)2&`MMyR)jHvqj=D_txTt=2AaG+IX0eK>Eq8g7yU~0 zBHN=K6_cbHK9Ar-&)aq_2_SPLk{>^ z?Xw@5b*5mG;n*45*-})C73R0!!+Soz9Z|amwq6~OyJh&L!3 zV(r62tRtI#ohZBwZ;kz3_6R$2VrHFOZbtfx5a|O=q~Gj>bPPZyH&Meg0v-bdhM+|U zjo%!dx;D@D_Eo&Fiw?5x7fO5#{3-f1MdB^gvsxqRdAp7zb5NioY4bz%KyIh;;3>x+ zYosDkYRj?1&@|9Vpd>@QQfD~RvaKb-ElHm@Qky@Ss^Oev<1o5`a7=o*YD%+g_y`Rf z_vyUO#cKwRwX%4h&h~O;iMvRY<*Hpl=)xg*wRqnuIT*z|ZSVAYDVQ?)(zK-2uKZ-= zqksE{lmB>8lH0Yi)OYjkpZ)E>c>dkW->v`k9~~L`tBXwMN2+`qT|AnzCFT>VzKG%f z3=JNU6Y?F@5gFKHBY;z^n59uZKsk+`ighnsyhnRhRp?J2a&!V7z5?9p%9qMRWlM9_ zy7oZbg2u1M4k=rDy}28~p2U^IN?m_bi76M9w$|8kY?oH-TeP(~ldIj0VH{n=Kh?J4 zc%nJIr-U1?Yj+buI}G1oOu6%iTS*@R&S1#zY^Ca2Jqe6bhn1~#JyuWFQ_JYTrPkv? zJyTBv^{#q`G|T6z9KagWWF?6GQP0r%;vm-Kjfd5B;S8e7pEvt8o%VVcz17!n8wYoK)g^=BU`(>q{5CY zAZ}f#Nt|!pFucvIXB0THYb;sDIzl1P%A1G^7b{2$7}oM~u~4LU%IIXtvl;mAMiE0e z=qxu32eAeKX9mk>Dp|vHjgp19c+OZ@MUOHE=rJzET@i? zzqF%lMng5pfdGikdyonV*G7>PtjVT5Rrmn`#;KT~;xZLiP~>$ewMi-kmFE4)b`fQT z1~L36^mfjoP`Xm;0KVObMbftqhrj0k0riO53ng|0=dM2VkraH{<}n7Ij^}$rG*uT$ z?1B0~b8*U|Unnv)dbYiCZ9Ip#BI!mRQ<9?aEJ3HkNo=)d{R%yr4j8#qPl1L4G;sWG zLnGp#&W|iyoAU!NR|$holV`&HejLGJrV|crF((iLnmr&u zPnmK)24#830FRVP$fZfobv})VB$8t0613H6e278WI+X@LlG}S zzm<{q%%sf{)MR^4Z>UUq5Ss8F)SU$lY6v96EQ?ID*Bz>8!?=eyWEKdGSq@T=o|g77 z@Is|kq&1%VZ)e>@^Q6Md6!ZhAB&c9HIM71)-dXiXMS6K9zoCFDHS7pS1Psl1LNpR#K%GfX~#+T7(!?aC^F zyc(9)OFYeU1w9EX+{|n}tcHfTQA@e>q3{h?Ml{(Pv<*CfDr9R!zQpGFy2=JqufT zwuqum@&<}&`5{#);@rMXN!ljt;F(~bdNkM*+NV?AKHbJ!+xCg{9ZUS5qe5{wFM;4m z{=uf~H+Kv6ks>B6`$6C4K6!h_yM_2&L_ciXHPR0_2?5@|&mH&k4|dqkznCRiV)^nk z0Mpmg0}O3d3{V8V@NurNLk|l_BG<{~NF=>Jt(rsFkX)qJ4eof>SgN^A-QqI=^*HNI zIiKnrHGZT0e>qEgpCHiJsFpG0DaPSa$GnNz1_EyLv`CW zq#kVL?!x*1u$?!4L)-cEbWb`GfqlDrJs^A1BfFuV^2nZ$KLptkcZze2zjM@yA|WhO z6q6#&chn2O|H-Ibfdb;wd#zmEfRVz@3$h8)a{MbS@Z@=9^1)H}dcfo?QzC(G{iH|a ziZh%^r@d~2kt-a-bcG}s7B9hPcJRg=-{0Vu!rt3DdoyIxUz3By+67S}$~3iU8kqxZ zl_{-2#~AZ)j0L1ZT|HCKnq&dGg87|+|$y6373{10e6JI z5;!N}x{MbeiPzBhmNe1SPM9IU+z4UlS(0Op>k8yu@%@)Z8YirZp!+u-_YkF;AM(e} z5g z9zuE8J%aM6dkp1s?s1gQy9UY=?n#s{xTjFQ=)Q#Vv^$70>z+aRvU?WgIX8!L$UR@g zv7e5_>#F=yI$yxQg|~)-HjdfNr>tuJ0#x8+h#%IB?pElQ8m-J5Hh{ZBSGBEI*nnOE zDUCs%j;NX)X3fq`d{=H43BWb~yVJ!t zsq(4jZ7B9H^O^AW4rdqgT^@jMv~a2+pY$((NCUjxNcvY1`07AD6J7z3FQPf0c?TgqN(AS$^=c$np$5q=RfGCnfXgAW_K4ynJ^w87E(-S2AHLOj0VVkr?Ad6g$%S z7*58Lj<@+tcsP}G*32ioB}qE}H8SBjPSUZg5ktC|bGG?Z^D=>?8v*%LGk3)4o_r=u z%1BD3puqA#SN4qeR7Q?|WUtG20~Mx5JzixdyNtei;TO2#vM|5HDjqGa=3 zyvu2~&8|thSD52+^JpMhM%{30xNdpNaphS940NXvXT_S0hx(22Z6s~n&O=_rQ*or$ znx9@&DA~X&j7 zF&r}xU#utbi^yY34;)K>%&8xQ^IP8yTQaWBs#a%JtFx-rN!1qhtuCm)u60HJyW8_+ zue%2(tq-{FMbMs9ASD9Q^~?kPA+8nCmGnQ~9osUL zYO1GN@aMPOewqcT6s#0pT{q23J&{uBX~V0#-m?s&+loy#X`T&KH2cnufeah2&_+Cn zos@QjIiPDp1!?d=&_tnXGzMEcM_N3OeXv|}Tsn+9W*G(InTF{^#>`?kW^dB$txA3Y zI+dHqHlv%*amJv>lEVmW;;`NoTWs za(-@fWMO8Oeaw7{+W3(?eM$2+(sb9GN0sbc>?L4A;IL3wWBJYYwn?Ha(a7DE>O^p% zm!2*KSAD~vK)$EBW-lHP2{BQ)NaiCUCJ%t%2_h^LxTKulQ=l%RK}s{<%WVOmU1|9W zjRH5$-;nB}$f?qgAZUyNo~hUo^@#2(v0v-S6ppbP{^_<^>aq4%i)*U(-#Th~-!b(V zoO9zlf_7^8v$EctSm2Ar7397w71(_~Z`fRq?T(OyAyTAsDtrFWIwmocf z*epuexXuX|uj`St$*H}=YM diff --git a/venv/Lib/site-packages/blendmodes/__pycache__/blendtype.cpython-39.pyc b/venv/Lib/site-packages/blendmodes/__pycache__/blendtype.cpython-39.pyc deleted file mode 100644 index c599cfb826eeeee7b7784bf75fcdf6e55c3f9253..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2850 zcmbVONpIUm6sA^^W7*mFG*NsgV4%*S$Dj?ONVFqVmI9h`(}7Td){Jc;6sdA?;#}K9 zZ~Yzp1^pBK1@GEZe?Tui_04dGWG_8bhB@C`W_fSkGXC~9G4S`-@4xuJ*@p3lAnLyk z60Z!Sk$#DQ8Ps46)(ji#CT%tZZiTINo315Odq5wthx9RfL_cGX z=@a%DeafEDK6^@^u|8V+4D8p!HV;knKeY|xzilVPp3dB(!KD9nfA@6#B8ro>AMSq9 z-&Kg%NArm{emnV2X{?1;HsO`fDKW7JqtVzKpEyd(XDrBqOD?oZOzh!oGMl@v=k8SL zt>Q4wlhZtjgl3J>x}(|XP%m1DS zVf;Y|gR zM*LhxmDsSv#GZ@~-*}Ez3WM{Dj7v9Yh?@@FZ%Pg3C+`adg@}DTbe&S3t`c6z6(aV# znX3%`7$;(TG2pb>fhTuY!Zbe10h&{4)+k~-?}n54aTQVg<0{NkEL7r*6@i{|j=ixCDaO+*kSYol^4VNx ziD1XM%n}XJYwmF1&ED$JNLGFpZ)9L7Sgcah5p%UtiHW_}{A8CU1-eB+LFN`Rb>#p{ zBPGzq0xXSCu#Gc{n-G-(NQF_jQwn8efUQC*2^13)s}caF0?0OzEs`JF;;Zyjwu7kv zvc=X_ZUd<>3WX&)p~2EfEhcZ0M_?*|Y~hX~ccEL1idiHqbc+$0UDU)}g%*C0>>*j^ zCD66WVQ~dgl~U*yCj?TJM6h(p15%YWsLB?uCJPgoV#ERjrYbXF z363ZxzZ%I9UsCe(_xIk5#VTfevAN1F;%MRLS-i%|z1qi8gO9g;k%zJZ?tkE?d9b+T z(d8lxP8V2_`ToXVz4y;~x{&WYyc-S0i|_g7D#3@5Vc~!JQh-K#x83-^e{!wqo|$}( zONmf8%dyK~C?ri}(2mdX^_aATu$5+sRIe;8&`3zFuZkdgDPERo7eDQ0ui0z;18nYT A{Qv*} diff --git a/venv/Lib/site-packages/blendmodes/blend.py b/venv/Lib/site-packages/blendmodes/blend.py deleted file mode 100644 index 4165bfa7..00000000 --- a/venv/Lib/site-packages/blendmodes/blend.py +++ /dev/null @@ -1,511 +0,0 @@ -"""Provide blending functions and types. - -Adapted from https://github.com/addisonElliott/pypdn/blob/master/pypdn/reader.py -and https://gitlab.com/inklabapp/pyora/-/blob/master/pyora/BlendNonSep.py -MIT License Copyright (c) 2020 FredHappyface - -Credits to: - -MIT License Copyright (c) 2019 Paul Jewell -For implementing blending from the Open Raster Image Spec - -MIT License Copyright (c) 2018 Addison Elliott -For implementing blending from Paint.NET - -MIT License Copyright (c) 2017 pashango -For implementing a number of blending functions used by other popular image -editors -""" - -from __future__ import annotations - -import warnings - -import numpy as np -from PIL import Image - -from .blendtype import BlendType - - -def normal(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.NORMAL.""" - del background # we don't care about this - return foreground - - -def multiply(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.MULTIPLY.""" - return np.clip(foreground * background, 0.0, 1.0) - - -def additive(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.ADDITIVE.""" - return np.minimum(background + foreground, 1.0) - - -def colourburn(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.COLOURBURN.""" - with np.errstate(divide="ignore"): - return np.where( - foreground != 0.0, np.maximum(1.0 - ((1.0 - background) / foreground), 0.0), 0.0 - ) - - -def colourdodge(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.COLOURDODGE.""" - with np.errstate(divide="ignore"): - return np.where(foreground != 1.0, np.minimum(background / (1.0 - foreground), 1.0), 1.0) - - -def reflect(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.REFLECT.""" - with np.errstate(divide="ignore"): - return np.where( - foreground != 1.0, np.minimum((background ** 2) / (1.0 - foreground), 1.0), 1.0 - ) - - -def glow(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.GLOW.""" - with np.errstate(divide="ignore"): - return np.where( - background != 1.0, np.minimum((foreground ** 2) / (1.0 - background), 1.0), 1.0 - ) - - -def overlay(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.OVERLAY.""" - return np.where( - background < 0.5, - 2 * background * foreground, - 1.0 - (2 * (1.0 - background) * (1.0 - foreground)), - ) - - -def difference(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.DIFFERENCE.""" - return np.abs(background - foreground) - - -def negation(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.NEGATION.""" - return np.maximum(background - foreground, 0.0) - - -def lighten(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.LIGHTEN.""" - return np.maximum(background, foreground) - - -def darken(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.DARKEN.""" - return np.minimum(background, foreground) - - -def screen(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.SCREEN.""" - return background + foreground - background * foreground - - -def xor(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.XOR.""" - # XOR requires int values so convert to uint8 - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - return imageIntToFloat(imageFloatToInt(background) ^ imageFloatToInt(foreground)) - - -def softlight(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.SOFTLIGHT.""" - return (1.0 - background) * background * foreground + background * ( - 1.0 - (1.0 - background) * (1.0 - foreground) - ) - - -def hardlight(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.HARDLIGHT.""" - return np.where( - foreground < 0.5, - np.minimum(background * 2 * foreground, 1.0), - np.minimum(1.0 - ((1.0 - background) * (1.0 - (foreground - 0.5) * 2.0)), 1.0), - ) - - -def grainextract(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.GRAINEXTRACT.""" - return np.clip(background - foreground + 0.5, 0.0, 1.0) - - -def grainmerge(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.GRAINMERGE.""" - return np.clip(background + foreground - 0.5, 0.0, 1.0) - - -def divide(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.DIVIDE.""" - return np.minimum((256.0 / 255.0 * background) / (1.0 / 255.0 + foreground), 1.0) - - -def pinlight(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.PINLIGHT.""" - return np.minimum(background, 2 * foreground) * (foreground < 0.5) + np.maximum( - background, 2 * (foreground - 0.5) - ) * (foreground >= 0.5) - - -def vividlight(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.VIVIDLIGHT.""" - return colourburn(background, foreground * 2) * (foreground < 0.5) + colourdodge( - background, 2 * (foreground - 0.5) - ) * (foreground >= 0.5) - - -def exclusion(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.EXCLUSION.""" - return background + foreground - (2.0 * background * foreground) - - -def _lum(colours: np.ndarray) -> np.ndarray: - """Luminosity. - - :param colours: x by x by 3 matrix of rgb color components of pixels - :return: x by x by 3 matrix of luminosity of pixels - """ - return (colours[:, :, 0] * 0.299) + (colours[:, :, 1] * 0.587) + (colours[:, :, 2] * 0.114) - - -def _setLum(originalColours: np.ndarray, newLuminosity: np.ndarray) -> np.ndarray: - """Set a new luminosity value for the matrix of color.""" - _colours = originalColours.copy() - _luminosity = _lum(_colours) - deltaLum = newLuminosity - _luminosity - _colours[:, :, 0] += deltaLum - _colours[:, :, 1] += deltaLum - _colours[:, :, 2] += deltaLum - _luminosity = _lum(_colours) - _minColours = np.min(_colours, axis=2) - _MaxColours = np.max(_colours, axis=2) - for i in range(_colours.shape[0]): - for j in range(_colours.shape[1]): - _colour = _colours[i][j] - newLuminosity = _luminosity[i, j] - minColour = _minColours[i, j] - maxColour = _MaxColours[i, j] - if minColour < 0: - _colours[i][j] = newLuminosity + ( - ((_colour - newLuminosity) * newLuminosity) / (newLuminosity - minColour) - ) - if maxColour > 1: - _colours[i][j] = newLuminosity + ( - ((_colour - newLuminosity) * (1 - newLuminosity)) / (maxColour - newLuminosity) - ) - return _colours - - -def _sat(colours: np.ndarray) -> np.ndarray: - """Saturation. - - :param colours: x by x by 3 matrix of rgb color components of pixels - :return: int of saturation of pixels - """ - return np.max(colours, axis=2) - np.min(colours, axis=2) - - -def _setSat(originalColours: np.ndarray, newSaturation: np.ndarray) -> np.ndarray: - """Set a new saturation value for the matrix of color. - - The current implementation cannot be vectorized in an efficient manner, - so it is very slow, - O(m*n) at least. This might be able to be improved with openCL if that is - the direction that the lib takes. - :param c: x by x by 3 matrix of rgb color components of pixels - :param s: int of the new saturation value for the matrix - :return: x by x by 3 matrix of luminosity of pixels - """ - _colours = originalColours.copy() - for i in range(_colours.shape[0]): - for j in range(_colours.shape[1]): - _colour = _colours[i][j] - minI = 0 - midI = 1 - maxI = 2 - if _colour[midI] < _colour[minI]: - minI, midI = midI, minI - if _colour[maxI] < _colour[midI]: - midI, maxI = maxI, midI - if _colour[midI] < _colour[minI]: - minI, midI = midI, minI - if _colour[maxI] - _colour[minI] > 0.0: - _colours[i][j][midI] = ((_colour[midI] - _colour[minI]) * newSaturation[i, j]) / ( - _colour[maxI] - _colour[minI] - ) - _colours[i][j][maxI] = newSaturation[i, j] - else: - _colours[i][j][midI] = 0 - _colours[i][j][maxI] = 0 - _colours[i][j][minI] = 0 - return _colours - - -def hue(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.HUE.""" - return _setLum(_setSat(foreground, _sat(background)), _lum(background)) - - -def saturation(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.SATURATION.""" - return _setLum(_setSat(background, _sat(foreground)), _lum(background)) - - -def colour(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.COLOUR.""" - return _setLum(foreground, _lum(background)) - - -def luminosity(background: np.ndarray, foreground: np.ndarray) -> np.ndarray: - """BlendType.LUMINOSITY.""" - return _setLum(background, _lum(foreground)) - - -def destin( - backgroundAlpha: np.ndarray, - foregroundAlpha: np.ndarray, - backgroundColour: np.ndarray, - foregroundColour: np.ndarray, -): - """'clip' composite mode. - - All parts of 'layer above' which are alpha in 'layer below' will be made - also alpha in 'layer above' - (to whatever degree of alpha they were) - - Destination which overlaps the source, replaces the source. - - Fa = 0; Fb = αs - co = αb x Cb x αs - αo = αb x αs - """ - del foregroundColour # Not used by function - outAlpha = backgroundAlpha * foregroundAlpha - with np.errstate(divide="ignore", invalid="ignore"): - outRGB = np.divide( - np.multiply((backgroundAlpha * foregroundAlpha)[:, :, None], backgroundColour), - outAlpha[:, :, None], - ) - return outRGB, outAlpha - - -def destout( - backgroundAlpha: np.ndarray, - foregroundAlpha: np.ndarray, - backgroundColour: np.ndarray, - foregroundColour: np.ndarray, -): - """Reverse 'Clip' composite mode. - - All parts of 'layer below' which are alpha in 'layer above' will be made - also alpha in 'layer below' - (to whatever degree of alpha they were) - - """ - del foregroundColour # Not used by function - outAlpha = backgroundAlpha * (1 - foregroundAlpha) - with np.errstate(divide="ignore", invalid="ignore"): - outRGB = np.divide( - np.multiply((backgroundAlpha * (1 - foregroundAlpha))[:, :, None], backgroundColour), - outAlpha[:, :, None], - ) - return outRGB, outAlpha - - -def destatop( - backgroundAlpha: np.ndarray, - foregroundAlpha: np.ndarray, - backgroundColour: np.ndarray, - foregroundColour: np.ndarray, -): - """Place the layer below above the 'layer above' in places where the 'layer above' exists... - - where 'layer below' does not exist, but 'layer above' does, place 'layer-above' - - """ - outAlpha = (foregroundAlpha * (1 - backgroundAlpha)) + (backgroundAlpha * foregroundAlpha) - with np.errstate(divide="ignore", invalid="ignore"): - outRGB = np.divide( - np.multiply((foregroundAlpha * (1 - backgroundAlpha))[:, :, None], foregroundColour) - + np.multiply((backgroundAlpha * foregroundAlpha)[:, :, None], backgroundColour), - outAlpha[:, :, None], - ) - return outRGB, outAlpha - - -def srcatop( - backgroundAlpha: np.ndarray, - foregroundAlpha: np.ndarray, - backgroundColour: np.ndarray, - foregroundColour: np.ndarray, -): - """Place the layer below above the 'layer above' in places where the 'layer above' exists.""" - outAlpha = (foregroundAlpha * backgroundAlpha) + (backgroundAlpha * (1 - foregroundAlpha)) - with np.errstate(divide="ignore", invalid="ignore"): - outRGB = np.divide( - np.multiply((foregroundAlpha * backgroundAlpha)[:, :, None], foregroundColour) - + np.multiply((backgroundAlpha * (1 - foregroundAlpha))[:, :, None], backgroundColour), - outAlpha[:, :, None], - ) - - return outRGB, outAlpha - - -def imageIntToFloat(image: np.ndarray) -> np.ndarray: - """Convert a numpy array representing an image to an array of floats. - - Args: - image (np.ndarray): numpy array of ints - - Returns: - np.ndarray: numpy array of floats - """ - return image / 255 - - -def imageFloatToInt(image: np.ndarray) -> np.ndarray: - """Convert a numpy array representing an image to an array of ints. - - Args: - image (np.ndarray): numpy array of floats - - Returns: - np.ndarray: numpy array of ints - """ - return (image * 255).astype(np.uint8) - - -def blend(background: np.ndarray, foreground: np.ndarray, blendType: BlendType) -> np.ndarray: - """Blend pixels. - - Args: - background (np.ndarray): background - foreground (np.ndarray): foreground - blendType (BlendType): the blend type - - Returns: - np.ndarray: new array representing the image - - background: np.ndarray, - foreground: np.ndarray and the return are in the form - - [[[0. 0. 0.] - [0. 0. 0.] - [0. 0. 0.] - ... - [0. 0. 0.] - [0. 0. 0.] - [0. 0. 0.]] - - ... - - [[0. 0. 0.] - [0. 0. 0.] - [0. 0. 0.] - ... - [0. 0. 0.] - [0. 0. 0.] - [0. 0. 0.]]] - """ - blendLookup = { - BlendType.NORMAL: normal, - BlendType.MULTIPLY: multiply, - BlendType.COLOURBURN: colourburn, - BlendType.COLOURDODGE: colourdodge, - BlendType.REFLECT: reflect, - BlendType.OVERLAY: overlay, - BlendType.DIFFERENCE: difference, - BlendType.LIGHTEN: lighten, - BlendType.DARKEN: darken, - BlendType.SCREEN: screen, - BlendType.SOFTLIGHT: softlight, - BlendType.HARDLIGHT: hardlight, - BlendType.GRAINEXTRACT: grainextract, - BlendType.GRAINMERGE: grainmerge, - BlendType.DIVIDE: divide, - BlendType.HUE: hue, - BlendType.SATURATION: saturation, - BlendType.COLOUR: colour, - BlendType.LUMINOSITY: luminosity, - BlendType.XOR: xor, - BlendType.NEGATION: negation, - BlendType.PINLIGHT: pinlight, - BlendType.VIVIDLIGHT: vividlight, - BlendType.EXCLUSION: exclusion, - } - - if blendType not in blendLookup: - return normal(background, foreground) - return blendLookup[blendType](background, foreground) - - -def blendLayers( - background: Image.Image, - foreground: Image.Image, - blendType: BlendType | tuple[str, ...], - opacity: float = 1.0, -) -> Image.Image: - """Blend layers using numpy array. - - Args: - background (Image.Image): background layer - foreground (Image.Image): foreground layer (must be same size as background) - blendType (BlendType): The blendtype - opacity (float): The opacity of the foreground image - - Returns: - Image.Image: combined image - """ - # Convert the Image.Image to a numpy array - npForeground: np.ndarray = imageIntToFloat(np.array(foreground.convert("RGBA"))) - npBackground: np.ndarray = imageIntToFloat(np.array(background.convert("RGBA"))) - - # Get the alpha from the layers - backgroundAlpha = npBackground[:, :, 3] - foregroundAlpha = npForeground[:, :, 3] * opacity - combinedAlpha = backgroundAlpha * foregroundAlpha - - # Get the colour from the layers - backgroundColor = npBackground[:, :, 0:3] - foregroundColor = npForeground[:, :, 0:3] - - # Some effects require alpha - alphaFunc = { - BlendType.DESTIN: destin, - BlendType.DESTOUT: destout, - BlendType.SRCATOP: srcatop, - BlendType.DESTATOP: destatop, - } - - if blendType in alphaFunc: - return Image.fromarray( - imageFloatToInt( - np.clip( - np.dstack( - alphaFunc[blendType]( - backgroundAlpha, foregroundAlpha, backgroundColor, foregroundColor - ) - ), - a_min=0, - a_max=1, - ) - ) - ) - - # Get the colours and the alpha for the new image - colorComponents = ( - (backgroundAlpha - combinedAlpha)[:, :, None] * backgroundColor - + (foregroundAlpha - combinedAlpha)[:, :, None] * foregroundColor - + combinedAlpha[:, :, None] * blend(backgroundColor, foregroundColor, blendType) - ) - alphaComponent = backgroundAlpha + foregroundAlpha - combinedAlpha - - return Image.fromarray( - imageFloatToInt(np.clip(np.dstack((colorComponents, alphaComponent)), a_min=0, a_max=1)) - ) diff --git a/venv/Lib/site-packages/blendmodes/blendtype.py b/venv/Lib/site-packages/blendmodes/blendtype.py deleted file mode 100644 index 1bde12a6..00000000 --- a/venv/Lib/site-packages/blendmodes/blendtype.py +++ /dev/null @@ -1,72 +0,0 @@ -"""Specify supported blend types.""" - -from __future__ import annotations - -from aenum import MultiValueEnum - - -class BlendType(str, MultiValueEnum): - """Specify supported blend types. - - NORMAL = "bm:normal", "normal" - MULTIPLY = "bm:multiply", "multiply" - ADDITIVE = "bm:additive", "additive" - COLOURBURN = "bm:colourburn", "colourburn" - COLOURDODGE = "bm:colourdodge", "colourdodge" - REFLECT = "bm:reflect", "reflect" - GLOW = "bm:glow", "glow" - OVERLAY = "bm:overlay", "overlay" - DIFFERENCE = "bm:difference", "difference" - NEGATION = "bm:negation", "negation" - LIGHTEN = "bm:lighten", "lighten" - DARKEN = "bm:darken", "darken" - SCREEN = "bm:screen", "screen" - XOR = "bm:xor", "xor" - SOFTLIGHT = "bm:softlight", "softlight" - HARDLIGHT = "bm:hardlight", "hardlight" - GRAINEXTRACT = "bm:grainextract", "grainextract" - GRAINMERGE = "bm:grainmerge", "grainmerge" - DIVIDE = "bm:divide", "divide" - HUE = "bm:hue", "hue" - SATURATION = "bm:saturation", "saturation" - COLOUR = "bm:colour", "colour" - LUMINOSITY = "bm:luminosity", "luminosity" - PINLIGHT = "bm:pinlight", "pinlight" - VIVIDLIGHT = "bm:vividlight", "vividlight" - EXCLUSION = "bm:exclusion", "exclusion" - DESTIN = "bm:destin", "destin" - DESTOUT = "bm:destout", "destout" - SRCATOP = "bm:srcatop", "srcatop" - DESTATOP = "bm:destatop", "destatop" - """ - - NORMAL = "bm:normal", "normal" - MULTIPLY = "bm:multiply", "multiply" - ADDITIVE = "bm:additive", "additive" - COLOURBURN = "bm:colourburn", "colourburn" - COLOURDODGE = "bm:colourdodge", "colourdodge" - REFLECT = "bm:reflect", "reflect" - GLOW = "bm:glow", "glow" - OVERLAY = "bm:overlay", "overlay" - DIFFERENCE = "bm:difference", "difference" - NEGATION = "bm:negation", "negation" - LIGHTEN = "bm:lighten", "lighten" - DARKEN = "bm:darken", "darken" - SCREEN = "bm:screen", "screen" - XOR = "bm:xor", "xor" - SOFTLIGHT = "bm:softlight", "softlight" - HARDLIGHT = "bm:hardlight", "hardlight" - GRAINEXTRACT = "bm:grainextract", "grainextract" - GRAINMERGE = "bm:grainmerge", "grainmerge" - DIVIDE = "bm:divide", "divide" - HUE = "bm:hue", "hue" - SATURATION = "bm:saturation", "saturation" - COLOUR = "bm:colour", "colour" - LUMINOSITY = "bm:luminosity", "luminosity" - PINLIGHT = "bm:pinlight", "pinlight" - VIVIDLIGHT = "bm:vividlight", "vividlight" - EXCLUSION = "bm:exclusion", "exclusion" - DESTIN = "bm:destin", "destin" - DESTOUT = "bm:destout", "destout" - SRCATOP = "bm:srcatop", "srcatop" - DESTATOP = "bm:destatop", "destatop" diff --git a/venv/Lib/site-packages/blendmodes/imagetools.py b/venv/Lib/site-packages/blendmodes/imagetools.py deleted file mode 100644 index 834b7c86..00000000 --- a/venv/Lib/site-packages/blendmodes/imagetools.py +++ /dev/null @@ -1,48 +0,0 @@ -"""Do stuff to images to prepare them. -""" -from __future__ import annotations - -import warnings - -from deprecation import deprecated -from PIL import Image - - -@deprecated(deprecated_in="2021.1", removed_in="", details="use renderWAlphaOffset") -def rasterImageOA( # pylint:disable=missing-function-docstring - image: Image.Image, size: tuple[int, int], alpha: float = 1.0, offsets: tuple[int, int] = (0, 0) -) -> Image.Image: - warnings.warn( - "Call to deprecated function rasterImageOA.", category=DeprecationWarning, stacklevel=2 - ) - return renderWAlphaOffset(image, size, alpha, offsets) - - -@deprecated(deprecated_in="2021.1", removed_in="", details="use renderWAlphaOffset") -def rasterImageOffset( # pylint:disable=missing-function-docstring - image: Image.Image, size: tuple[int, int], offsets: tuple[int, int] = (0, 0) -) -> Image.Image: - warnings.warn( - "Call to deprecated function rasterImageOffset.", category=DeprecationWarning, stacklevel=2 - ) - return renderWAlphaOffset(image, size, 1, offsets) - - -def renderWAlphaOffset( - image: Image.Image, size: tuple[int, int], alpha: float = 1.0, offsets: tuple[int, int] = (0, 0) -) -> Image.Image: - """Render an image with offset and alpha to a given size. - - Args: - image (Image.Image): pil image to draw - size (tuple[int, int]): width, height as a tuple - alpha (float, optional): alpha transparency. Defaults to 1.0. - offsets (tuple[int, int], optional): x, y offsets as a tuple. - Defaults to (0, 0). - - Returns: - Image.Image: new image - """ - imageOffset = Image.new("RGBA", size) - imageOffset.paste(image.convert("RGBA"), offsets, image.convert("RGBA")) - return Image.blend(Image.new("RGBA", size), imageOffset, alpha) From 9bcf4165f89e6706a25ef0c32149d1fd3f4e102a Mon Sep 17 00:00:00 2001 From: ThereforeGames <95403634+ThereforeGames@users.noreply.github.com> Date: Sun, 11 Dec 2022 18:09:18 -0500 Subject: [PATCH 16/59] Update requirements.txt --- requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requirements.txt b/requirements.txt index 678acb4d..85e51575 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ +aenum +blendmodes accelerate basicsr fairscale==0.4.4 From cb64439f41bfaec168704751609d4afa47d6f2e9 Mon Sep 17 00:00:00 2001 From: Fionn Langhans Date: Mon, 12 Dec 2022 21:18:25 +0100 Subject: [PATCH 17/59] Bugfix: Use /usr/bin/env bash instead of just /bin/bash The problem: Some Linux distrubutions, like NixOS, use a non-standard filesystem. This causes the bash program to not be at /bin/bash (though /usr/bin/env is always there). --- webui.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webui.sh b/webui.sh index 683c97d3..04ecbf76 100755 --- a/webui.sh +++ b/webui.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash ################################################# # Please do not make any changes to this file, # # change the variables in webui-user.sh instead # From 27c0504bc4d17eec6e58148ab33c75f5ed2e6f00 Mon Sep 17 00:00:00 2001 From: David Vorick Date: Tue, 13 Dec 2022 12:03:16 -0500 Subject: [PATCH 18/59] add support for prompts, negative prompts, and sampler-by-name in text file script --- scripts/prompts_from_file.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/scripts/prompts_from_file.py b/scripts/prompts_from_file.py index 32fe6bdb..6e118ddb 100644 --- a/scripts/prompts_from_file.py +++ b/scripts/prompts_from_file.py @@ -9,6 +9,7 @@ import shlex import modules.scripts as scripts import gradio as gr +from modules import sd_samplers from modules.processing import Processed, process_images from PIL import Image from modules.shared import opts, cmd_opts, state @@ -44,6 +45,7 @@ prompt_tags = { "seed_resize_from_h": process_int_tag, "seed_resize_from_w": process_int_tag, "sampler_index": process_int_tag, + "sampler_name": process_string_tag, "batch_size": process_int_tag, "n_iter": process_int_tag, "steps": process_int_tag, @@ -66,14 +68,28 @@ def cmdargs(line): arg = args[pos] assert arg.startswith("--"), f'must start with "--": {arg}' + assert pos+1 < len(args), f'missing argument for command line option {arg}' + tag = arg[2:] + if tag == "prompt" or tag == "negative_prompt": + pos += 1 + prompt = args[pos] + pos += 1 + while pos < len(args) and not args[pos].startswith("--"): + prompt += " " + prompt += args[pos] + pos += 1 + res[tag] = prompt + continue + + func = prompt_tags.get(tag, None) assert func, f'unknown commandline option: {arg}' - assert pos+1 < len(args), f'missing argument for command line option {arg}' - val = args[pos+1] + if tag == "sampler_name": + val = sd_samplers.samplers_map.get(val.lower(), None) res[tag] = func(val) From 7077428209cd02f7da23ef843e5027e960f6aa39 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Tue, 13 Dec 2022 13:05:40 -0800 Subject: [PATCH 19/59] Save hypernetwork hash in infotext --- modules/processing.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/processing.py b/modules/processing.py index 24c537d1..6dd7491b 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -314,7 +314,7 @@ class Processed: return json.dumps(obj) - def infotext(self, p: StableDiffusionProcessing, index): + def infotext(self, p: StableDiffusionProcessing, index): return create_infotext(p, self.all_prompts, self.all_seeds, self.all_subseeds, comments=[], position_in_batch=index % self.batch_size, iteration=index // self.batch_size) @@ -429,6 +429,7 @@ def create_infotext(p, all_prompts, all_seeds, all_subseeds, comments, iteration "Model hash": getattr(p, 'sd_model_hash', None if not opts.add_model_hash_to_info or not shared.sd_model.sd_model_hash else shared.sd_model.sd_model_hash), "Model": (None if not opts.add_model_name_to_info or not shared.sd_model.sd_checkpoint_info.model_name else shared.sd_model.sd_checkpoint_info.model_name.replace(',', '').replace(':', '')), "Hypernet": (None if shared.loaded_hypernetwork is None else shared.loaded_hypernetwork.name), + "Hypernet hash": (None if shared.loaded_hypernetwork is None else sd_models.model_hash(shared.loaded_hypernetwork.filename)), "Hypernet strength": (None if shared.loaded_hypernetwork is None or shared.opts.sd_hypernetwork_strength >= 1 else shared.opts.sd_hypernetwork_strength), "Batch size": (None if p.batch_size < 2 else p.batch_size), "Batch pos": (None if p.batch_size < 2 else position_in_batch), @@ -446,7 +447,7 @@ def create_infotext(p, all_prompts, all_seeds, all_subseeds, comments, iteration generation_params_text = ", ".join([k if k == v else f'{k}: {generation_parameters_copypaste.quote(v)}' for k, v in generation_params.items() if v is not None]) - negative_prompt_text = "\nNegative prompt: " + p.all_negative_prompts[index] if p.all_negative_prompts[index] else "" + negative_prompt_text = "\nNegative prompt: " + p.all_negative_prompts[index] if p.all_negative_prompts[index] else "" return f"{all_prompts[index]}{negative_prompt_text}\n{generation_params_text}".strip() From 9d5948e5f7324b98fa7445accb2fe14487ff809d Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Tue, 13 Dec 2022 14:25:16 -0800 Subject: [PATCH 20/59] Correctly restore hypernetwork from hash --- modules/generation_parameters_copypaste.py | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py index 565e342d..e4e1d41c 100644 --- a/modules/generation_parameters_copypaste.py +++ b/modules/generation_parameters_copypaste.py @@ -14,6 +14,7 @@ re_param_code = r'\s*([\w ]+):\s*("(?:\\|\"|[^\"])+"|[^,]*)(?:,|$)' re_param = re.compile(re_param_code) re_params = re.compile(r"^(?:" + re_param_code + "){3,}$") re_imagesize = re.compile(r"^(\d+)x(\d+)$") +re_hypernet_hash = re.compile("\(([0-9a-f]+)\)$") type_of_gr_update = type(gr.update()) paste_fields = {} bind_list = [] @@ -139,6 +140,30 @@ def run_bind(): ) +def find_hypernetwork_key(hypernet_name, hypernet_hash=None): + """Determines the config parameter name to use for the hypernet based on the parameters in the infotext. + + Example: an infotext provides "Hypernet: ke-ta" and "Hypernet hash: 1234abcd". For the "Hypernet" config + parameter this means there should be an entry that looks like "ke-ta-10000(1234abcd)" to set it to. + + If the infotext has no hash, then a hypernet with the same name and the most steps will be selected instead. + """ + hypernet_name = hypernet_name.lower() + if hypernet_hash is not None: + # Try to match the hash in the name + for hypernet_key in shared.hypernetworks.keys(): + result = re_hypernet_hash.search(hypernet_key) + if result is not None and result[1] == hypernet_hash: + return hypernet_key + else: + # Fall back to a hypernet with the same name + for hypernet_key in shared.hypernetworks.keys(): + if hypernet_key.lower().startswith(hypernet_name): + return hypernet_key + + return None + + def parse_generation_parameters(x: str): """parses generation parameters string, the one you see in text field under the picture in UI: ``` @@ -188,6 +213,11 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model if "Clip skip" not in res: res["Clip skip"] = "1" + if "Hypernet" in res: + hypernet_name = res["Hypernet"] + hypernet_hash = res.get("Hypernet hash", None) + res["Hypernet"] = find_hypernetwork_key(hypernet_name, hypernet_hash) + return res From 1fcb9595143fc352240635959ea5b9929c02dca6 Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Tue, 13 Dec 2022 14:30:54 -0800 Subject: [PATCH 21/59] Correctly restore default hypernetwork strength --- modules/generation_parameters_copypaste.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py index e4e1d41c..a33f8d5c 100644 --- a/modules/generation_parameters_copypaste.py +++ b/modules/generation_parameters_copypaste.py @@ -213,6 +213,9 @@ Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 965400086, Size: 512x512, Model if "Clip skip" not in res: res["Clip skip"] = "1" + if "Hypernet strength" not in res: + res["Hypernet strength"] = "1" + if "Hypernet" in res: hypernet_name = res["Hypernet"] hypernet_hash = res.get("Hypernet hash", None) From 5f407ebd61bb5c1ca025c5d7fa642e32ac0526ce Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Tue, 13 Dec 2022 14:32:26 -0800 Subject: [PATCH 22/59] Fix comment --- modules/generation_parameters_copypaste.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py index a33f8d5c..fbd91300 100644 --- a/modules/generation_parameters_copypaste.py +++ b/modules/generation_parameters_copypaste.py @@ -146,7 +146,7 @@ def find_hypernetwork_key(hypernet_name, hypernet_hash=None): Example: an infotext provides "Hypernet: ke-ta" and "Hypernet hash: 1234abcd". For the "Hypernet" config parameter this means there should be an entry that looks like "ke-ta-10000(1234abcd)" to set it to. - If the infotext has no hash, then a hypernet with the same name and the most steps will be selected instead. + If the infotext has no hash, then a hypernet with the same name will be selected instead. """ hypernet_name = hypernet_name.lower() if hypernet_hash is not None: From 957e15c4642199e0792eba817a15e244246fb012 Mon Sep 17 00:00:00 2001 From: Yuval Aboulafia Date: Wed, 14 Dec 2022 20:59:33 +0200 Subject: [PATCH 23/59] Correct singleton comparisons --- modules/extras.py | 2 +- modules/ngrok.py | 4 ++-- modules/ui.py | 8 ++++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/modules/extras.py b/modules/extras.py index 0ad8deec..69b85465 100644 --- a/modules/extras.py +++ b/modules/extras.py @@ -188,7 +188,7 @@ def run_extras(extras_mode, resize_mode, image, image_folder, input_dir, output_ for op in extras_ops: image, info = op(image, info) - if opts.use_original_name_batch and image_name != None: + if opts.use_original_name_batch and image_name is not None: basename = os.path.splitext(os.path.basename(image_name))[0] else: basename = '' diff --git a/modules/ngrok.py b/modules/ngrok.py index 64c9a3c2..3df2c06b 100644 --- a/modules/ngrok.py +++ b/modules/ngrok.py @@ -2,7 +2,7 @@ from pyngrok import ngrok, conf, exception def connect(token, port, region): account = None - if token == None: + if token is None: token = 'None' else: if ':' in token: @@ -14,7 +14,7 @@ def connect(token, port, region): auth_token=token, region=region ) try: - if account == None: + if account is None: public_url = ngrok.connect(port, pyngrok_config=config, bind_tls=True).public_url else: public_url = ngrok.connect(port, pyngrok_config=config, bind_tls=True, auth=account).public_url diff --git a/modules/ui.py b/modules/ui.py index 28481e33..c4bb186d 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -49,10 +49,14 @@ if not cmd_opts.share and not cmd_opts.listen: gradio.utils.version_check = lambda: None gradio.utils.get_local_ip_address = lambda: '127.0.0.1' -if cmd_opts.ngrok != None: +if cmd_opts.ngrok is not None: import modules.ngrok as ngrok print('ngrok authtoken detected, trying to connect...') - ngrok.connect(cmd_opts.ngrok, cmd_opts.port if cmd_opts.port != None else 7860, cmd_opts.ngrok_region) + ngrok.connect( + cmd_opts.ngrok, + cmd_opts.port if cmd_opts.port is not None else 7860, + cmd_opts.ngrok_region + ) def gr_show(visible=True): From c0355caefe3d82e304e6d832699d581fc8f9fbf9 Mon Sep 17 00:00:00 2001 From: Jim Hays Date: Wed, 14 Dec 2022 21:01:32 -0500 Subject: [PATCH 24/59] Fix various typos --- README.md | 4 ++-- javascript/contextMenus.js | 24 +++++++++---------- javascript/progressbar.js | 12 +++++----- javascript/ui.js | 2 +- modules/api/api.py | 18 +++++++------- modules/api/models.py | 2 +- modules/images.py | 4 ++-- modules/processing.py | 14 +++++------ modules/safe.py | 4 ++-- modules/scripts.py | 4 ++-- modules/sd_hijack_inpainting.py | 6 ++--- modules/sd_hijack_unet.py | 2 +- modules/textual_inversion/dataset.py | 10 ++++---- .../textual_inversion/textual_inversion.py | 16 ++++++------- scripts/prompt_matrix.py | 10 ++++---- webui.py | 4 ++-- 16 files changed, 68 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index 55990581..556000fb 100644 --- a/README.md +++ b/README.md @@ -82,8 +82,8 @@ Check the [custom scripts](https://github.com/AUTOMATIC1111/stable-diffusion-web - Use VAEs - Estimated completion time in progress bar - API -- Support for dedicated [inpainting model](https://github.com/runwayml/stable-diffusion#inpainting-with-stable-diffusion) by RunwayML. -- via extension: [Aesthetic Gradients](https://github.com/AUTOMATIC1111/stable-diffusion-webui-aesthetic-gradients), a way to generate images with a specific aesthetic by using clip images embds (implementation of [https://github.com/vicgalle/stable-diffusion-aesthetic-gradients](https://github.com/vicgalle/stable-diffusion-aesthetic-gradients)) +- Support for dedicated [inpainting model](https://github.com/runwayml/stable-diffusion#inpainting-with-stable-diffusion) by RunwayML. +- via extension: [Aesthetic Gradients](https://github.com/AUTOMATIC1111/stable-diffusion-webui-aesthetic-gradients), a way to generate images with a specific aesthetic by using clip images embeds (implementation of [https://github.com/vicgalle/stable-diffusion-aesthetic-gradients](https://github.com/vicgalle/stable-diffusion-aesthetic-gradients)) - [Stable Diffusion 2.0](https://github.com/Stability-AI/stablediffusion) support - see [wiki](https://github.com/AUTOMATIC1111/stable-diffusion-webui/wiki/Features#stable-diffusion-20) for instructions ## Installation and Running diff --git a/javascript/contextMenus.js b/javascript/contextMenus.js index fe67c42e..11bcce1b 100644 --- a/javascript/contextMenus.js +++ b/javascript/contextMenus.js @@ -9,7 +9,7 @@ contextMenuInit = function(){ function showContextMenu(event,element,menuEntries){ let posx = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; - let posy = event.clientY + document.body.scrollTop + document.documentElement.scrollTop; + let posy = event.clientY + document.body.scrollTop + document.documentElement.scrollTop; let oldMenu = gradioApp().querySelector('#context-menu') if(oldMenu){ @@ -61,15 +61,15 @@ contextMenuInit = function(){ } - function appendContextMenuOption(targetEmementSelector,entryName,entryFunction){ - - currentItems = menuSpecs.get(targetEmementSelector) - + function appendContextMenuOption(targetElementSelector,entryName,entryFunction){ + + currentItems = menuSpecs.get(targetElementSelector) + if(!currentItems){ currentItems = [] - menuSpecs.set(targetEmementSelector,currentItems); + menuSpecs.set(targetElementSelector,currentItems); } - let newItem = {'id':targetEmementSelector+'_'+uid(), + let newItem = {'id':targetElementSelector+'_'+uid(), 'name':entryName, 'func':entryFunction, 'isNew':true} @@ -97,7 +97,7 @@ contextMenuInit = function(){ if(source.id && source.id.indexOf('check_progress')>-1){ return } - + let oldMenu = gradioApp().querySelector('#context-menu') if(oldMenu){ oldMenu.remove() @@ -117,7 +117,7 @@ contextMenuInit = function(){ }) }); eventListenerApplied=true - + } return [appendContextMenuOption, removeContextMenuOption, addContextMenuEventListener] @@ -152,8 +152,8 @@ addContextMenuEventListener = initResponse[2]; generateOnRepeat('#img2img_generate','#img2img_interrupt'); }) - let cancelGenerateForever = function(){ - clearInterval(window.generateOnRepeatInterval) + let cancelGenerateForever = function(){ + clearInterval(window.generateOnRepeatInterval) } appendContextMenuOption('#txt2img_interrupt','Cancel generate forever',cancelGenerateForever) @@ -162,7 +162,7 @@ addContextMenuEventListener = initResponse[2]; appendContextMenuOption('#img2img_generate', 'Cancel generate forever',cancelGenerateForever) appendContextMenuOption('#roll','Roll three', - function(){ + function(){ let rollbutton = get_uiCurrentTabContent().querySelector('#roll'); setTimeout(function(){rollbutton.click()},100) setTimeout(function(){rollbutton.click()},200) diff --git a/javascript/progressbar.js b/javascript/progressbar.js index d58737c4..d6323ed9 100644 --- a/javascript/progressbar.js +++ b/javascript/progressbar.js @@ -3,7 +3,7 @@ global_progressbars = {} galleries = {} galleryObservers = {} -// this tracks laumnches of window.setTimeout for progressbar to prevent starting a new timeout when the previous is still running +// this tracks launches of window.setTimeout for progressbar to prevent starting a new timeout when the previous is still running timeoutIds = {} function check_progressbar(id_part, id_progressbar, id_progressbar_span, id_skip, id_interrupt, id_preview, id_gallery){ @@ -20,21 +20,21 @@ function check_progressbar(id_part, id_progressbar, id_progressbar_span, id_skip var skip = id_skip ? gradioApp().getElementById(id_skip) : null var interrupt = gradioApp().getElementById(id_interrupt) - + if(opts.show_progress_in_title && progressbar && progressbar.offsetParent){ if(progressbar.innerText){ let newtitle = '[' + progressbar.innerText.trim() + '] Stable Diffusion'; if(document.title != newtitle){ - document.title = newtitle; + document.title = newtitle; } }else{ let newtitle = 'Stable Diffusion' if(document.title != newtitle){ - document.title = newtitle; + document.title = newtitle; } } } - + if(progressbar!= null && progressbar != global_progressbars[id_progressbar]){ global_progressbars[id_progressbar] = progressbar @@ -63,7 +63,7 @@ function check_progressbar(id_part, id_progressbar, id_progressbar_span, id_skip skip.style.display = "none" } interrupt.style.display = "none" - + //disconnect observer once generation finished, so user can close selected image if they want if (galleryObservers[id_gallery]) { galleryObservers[id_gallery].disconnect(); diff --git a/javascript/ui.js b/javascript/ui.js index 2cb280e5..587dd782 100644 --- a/javascript/ui.js +++ b/javascript/ui.js @@ -100,7 +100,7 @@ function create_submit_args(args){ // As it is currently, txt2img and img2img send back the previous output args (txt2img_gallery, generation_info, html_info) whenever you generate a new image. // This can lead to uploading a huge gallery of previously generated images, which leads to an unnecessary delay between submitting and beginning to generate. - // I don't know why gradio is seding outputs along with inputs, but we can prevent sending the image gallery here, which seems to be an issue for some. + // I don't know why gradio is sending outputs along with inputs, but we can prevent sending the image gallery here, which seems to be an issue for some. // If gradio at some point stops sending outputs, this may break something if(Array.isArray(res[res.length - 3])){ res[res.length - 3] = null diff --git a/modules/api/api.py b/modules/api/api.py index 89935a70..33845045 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -67,10 +67,10 @@ def encode_pil_to_base64(image): class Api: def __init__(self, app: FastAPI, queue_lock: Lock): if shared.cmd_opts.api_auth: - self.credenticals = dict() + self.credentials = dict() for auth in shared.cmd_opts.api_auth.split(","): user, password = auth.split(":") - self.credenticals[user] = password + self.credentials[user] = password self.router = APIRouter() self.app = app @@ -93,7 +93,7 @@ class Api: self.add_api_route("/sdapi/v1/hypernetworks", self.get_hypernetworks, methods=["GET"], response_model=List[HypernetworkItem]) self.add_api_route("/sdapi/v1/face-restorers", self.get_face_restorers, methods=["GET"], response_model=List[FaceRestorerItem]) self.add_api_route("/sdapi/v1/realesrgan-models", self.get_realesrgan_models, methods=["GET"], response_model=List[RealesrganItem]) - self.add_api_route("/sdapi/v1/prompt-styles", self.get_promp_styles, methods=["GET"], response_model=List[PromptStyleItem]) + self.add_api_route("/sdapi/v1/prompt-styles", self.get_prompt_styles, methods=["GET"], response_model=List[PromptStyleItem]) self.add_api_route("/sdapi/v1/artist-categories", self.get_artists_categories, methods=["GET"], response_model=List[str]) self.add_api_route("/sdapi/v1/artists", self.get_artists, methods=["GET"], response_model=List[ArtistItem]) @@ -102,9 +102,9 @@ class Api: return self.app.add_api_route(path, endpoint, dependencies=[Depends(self.auth)], **kwargs) return self.app.add_api_route(path, endpoint, **kwargs) - def auth(self, credenticals: HTTPBasicCredentials = Depends(HTTPBasic())): - if credenticals.username in self.credenticals: - if compare_digest(credenticals.password, self.credenticals[credenticals.username]): + def auth(self, credentials: HTTPBasicCredentials = Depends(HTTPBasic())): + if credentials.username in self.credentials: + if compare_digest(credentials.password, self.credentials[credentials.username]): return True raise HTTPException(status_code=401, detail="Incorrect username or password", headers={"WWW-Authenticate": "Basic"}) @@ -239,7 +239,7 @@ class Api: def interrogateapi(self, interrogatereq: InterrogateRequest): image_b64 = interrogatereq.image if image_b64 is None: - raise HTTPException(status_code=404, detail="Image not found") + raise HTTPException(status_code=404, detail="Image not found") img = decode_base64_to_image(image_b64) img = img.convert('RGB') @@ -252,7 +252,7 @@ class Api: processed = deepbooru.model.tag(img) else: raise HTTPException(status_code=404, detail="Model not found") - + return InterrogateResponse(caption=processed) def interruptapi(self): @@ -308,7 +308,7 @@ class Api: def get_realesrgan_models(self): return [{"name":x.name,"path":x.data_path, "scale":x.scale} for x in get_realesrgan_models(None)] - def get_promp_styles(self): + def get_prompt_styles(self): styleList = [] for k in shared.prompt_styles.styles: style = shared.prompt_styles.styles[k] diff --git a/modules/api/models.py b/modules/api/models.py index f77951fc..a22bc6b3 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -128,7 +128,7 @@ class ExtrasBaseRequest(BaseModel): upscaling_resize: float = Field(default=2, title="Upscaling Factor", ge=1, le=4, description="By how much to upscale the image, only used when resize_mode=0.") upscaling_resize_w: int = Field(default=512, title="Target Width", ge=1, description="Target width for the upscaler to hit. Only used when resize_mode=1.") upscaling_resize_h: int = Field(default=512, title="Target Height", ge=1, description="Target height for the upscaler to hit. Only used when resize_mode=1.") - upscaling_crop: bool = Field(default=True, title="Crop to fit", description="Should the upscaler crop the image to fit in the choosen size?") + upscaling_crop: bool = Field(default=True, title="Crop to fit", description="Should the upscaler crop the image to fit in the chosen size?") upscaler_1: str = Field(default="None", title="Main upscaler", description=f"The name of the main upscaler to use, it has to be one of this list: {' , '.join([x.name for x in sd_upscalers])}") upscaler_2: str = Field(default="None", title="Secondary upscaler", description=f"The name of the secondary upscaler to use, it has to be one of this list: {' , '.join([x.name for x in sd_upscalers])}") extras_upscaler_2_visibility: float = Field(default=0, title="Secondary upscaler visibility", ge=0, le=1, allow_inf_nan=False, description="Sets the visibility of secondary upscaler, values should be between 0 and 1.") diff --git a/modules/images.py b/modules/images.py index 8146f580..93a14289 100644 --- a/modules/images.py +++ b/modules/images.py @@ -429,7 +429,7 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='png', i The directory to save the image. Note, the option `save_to_dirs` will make the image to be saved into a sub directory. basename (`str`): The base filename which will be applied to `filename pattern`. - seed, prompt, short_filename, + seed, prompt, short_filename, extension (`str`): Image file extension, default is `png`. pngsectionname (`str`): @@ -590,7 +590,7 @@ def read_info_from_image(image): Negative prompt: {json_info["uc"]} Steps: {json_info["steps"]}, Sampler: {sampler}, CFG scale: {json_info["scale"]}, Seed: {json_info["seed"]}, Size: {image.width}x{image.height}, Clip skip: 2, ENSD: 31337""" except Exception: - print(f"Error parsing NovelAI iamge generation parameters:", file=sys.stderr) + print(f"Error parsing NovelAI image generation parameters:", file=sys.stderr) print(traceback.format_exc(), file=sys.stderr) return geninfo, items diff --git a/modules/processing.py b/modules/processing.py index 24c537d1..fe7f4faf 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -147,11 +147,11 @@ class StableDiffusionProcessing(): # The "masked-image" in this case will just be all zeros since the entire image is masked. image_conditioning = torch.zeros(x.shape[0], 3, height, width, device=x.device) - image_conditioning = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(image_conditioning)) + image_conditioning = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(image_conditioning)) # Add the fake full 1s mask to the first dimension. image_conditioning = torch.nn.functional.pad(image_conditioning, (0, 0, 0, 0, 1, 0), value=1.0) - image_conditioning = image_conditioning.to(x.dtype) + image_conditioning = image_conditioning.to(x.dtype) return image_conditioning @@ -199,7 +199,7 @@ class StableDiffusionProcessing(): source_image * (1.0 - conditioning_mask), getattr(self, "inpainting_mask_weight", shared.opts.inpainting_mask_weight) ) - + # Encode the new masked image using first stage of network. conditioning_image = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(conditioning_image)) @@ -537,7 +537,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: for n in range(p.n_iter): if state.skipped: state.skipped = False - + if state.interrupted: break @@ -612,7 +612,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: image.info["parameters"] = text output_images.append(image) - del x_samples_ddim + del x_samples_ddim devices.torch_gc() @@ -704,7 +704,7 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): samples = samples[:, :, self.truncate_y//2:samples.shape[2]-self.truncate_y//2, self.truncate_x//2:samples.shape[3]-self.truncate_x//2] - """saves image before applying hires fix, if enabled in options; takes as an arguyment either an image or batch with latent space images""" + """saves image before applying hires fix, if enabled in options; takes as an argument either an image or batch with latent space images""" def save_intermediate(image, index): if not opts.save or self.do_not_save_samples or not opts.save_images_before_highres_fix: return @@ -720,7 +720,7 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): samples = torch.nn.functional.interpolate(samples, size=(self.height // opt_f, self.width // opt_f), mode="bilinear") - # Avoid making the inpainting conditioning unless necessary as + # Avoid making the inpainting conditioning unless necessary as # this does need some extra compute to decode / encode the image again. if getattr(self, "inpainting_mask_weight", shared.opts.inpainting_mask_weight) < 1.0: image_conditioning = self.img2img_image_conditioning(decode_first_stage(self.sd_model, samples), samples) diff --git a/modules/safe.py b/modules/safe.py index 10460ad0..20e9d2fa 100644 --- a/modules/safe.py +++ b/modules/safe.py @@ -80,7 +80,7 @@ def check_pt(filename, extra_handler): # new pytorch format is a zip file with zipfile.ZipFile(filename) as z: check_zip_filenames(filename, z.namelist()) - + # find filename of data.pkl in zip file: '/data.pkl' data_pkl_filenames = [f for f in z.namelist() if data_pkl_re.match(f)] if len(data_pkl_filenames) == 0: @@ -108,7 +108,7 @@ def load(filename, *args, **kwargs): def load_with_extra(filename, extra_handler=None, *args, **kwargs): """ - this functon is intended to be used by extensions that want to load models with + this function is intended to be used by extensions that want to load models with some extra classes in them that the usual unpickler would find suspicious. Use the extra_handler argument to specify a function that takes module and field name as text, diff --git a/modules/scripts.py b/modules/scripts.py index 23ca195d..722f8685 100644 --- a/modules/scripts.py +++ b/modules/scripts.py @@ -36,7 +36,7 @@ class Script: def ui(self, is_img2img): """this function should create gradio UI elements. See https://gradio.app/docs/#components The return value should be an array of all components that are used in processing. - Values of those returned componenbts will be passed to run() and process() functions. + Values of those returned components will be passed to run() and process() functions. """ pass @@ -47,7 +47,7 @@ class Script: This function should return: - False if the script should not be shown in UI at all - - True if the script should be shown in UI if it's scelected in the scripts drowpdown + - True if the script should be shown in UI if it's selected in the scripts dropdown - script.AlwaysVisible if the script should be shown in UI at all times """ diff --git a/modules/sd_hijack_inpainting.py b/modules/sd_hijack_inpainting.py index 938f9a58..d72f83fd 100644 --- a/modules/sd_hijack_inpainting.py +++ b/modules/sd_hijack_inpainting.py @@ -209,7 +209,7 @@ def p_sample_plms(self, x, c, t, index, repeat_noise=False, use_original_steps=F else: x_in = torch.cat([x] * 2) t_in = torch.cat([t] * 2) - + if isinstance(c, dict): assert isinstance(unconditional_conditioning, dict) c_in = dict() @@ -278,7 +278,7 @@ def p_sample_plms(self, x, c, t, index, repeat_noise=False, use_original_steps=F x_prev, pred_x0 = get_x_prev_and_pred_x0(e_t_prime, index) return x_prev, pred_x0, e_t - + # ================================================================================================= # Monkey patch LatentInpaintDiffusion to load the checkpoint with a proper config. # Adapted from: @@ -326,7 +326,7 @@ def do_inpainting_hijack(): # most of this stuff seems to no longer be needed because it is already included into SD2.0 # LatentInpaintDiffusion remains because SD2.0's LatentInpaintDiffusion can't be loaded without specifying a checkpoint # p_sample_plms is needed because PLMS can't work with dicts as conditionings - # this file should be cleaned up later if weverything tuens out to work fine + # this file should be cleaned up later if everything turns out to work fine # ldm.models.diffusion.ddpm.get_unconditional_conditioning = get_unconditional_conditioning ldm.models.diffusion.ddpm.LatentInpaintDiffusion = LatentInpaintDiffusion diff --git a/modules/sd_hijack_unet.py b/modules/sd_hijack_unet.py index 1b9d7757..18daf8c1 100644 --- a/modules/sd_hijack_unet.py +++ b/modules/sd_hijack_unet.py @@ -4,7 +4,7 @@ import torch class TorchHijackForUnet: """ This is torch, but with cat that resizes tensors to appropriate dimensions if they do not match; - this makes it possible to create pictures with dimensions that are muliples of 8 rather than 64 + this makes it possible to create pictures with dimensions that are multiples of 8 rather than 64 """ def __getattr__(self, item): diff --git a/modules/textual_inversion/dataset.py b/modules/textual_inversion/dataset.py index 2dc64c3c..88d68c76 100644 --- a/modules/textual_inversion/dataset.py +++ b/modules/textual_inversion/dataset.py @@ -28,9 +28,9 @@ class DatasetEntry: class PersonalizedBase(Dataset): - def __init__(self, data_root, width, height, repeats, flip_p=0.5, placeholder_token="*", model=None, cond_model=None, device=None, template_file=None, include_cond=False, batch_size=1, gradient_step=1, shuffle_tags=False, tag_drop_out=0, latent_sampling_method='once'): + def __init__(self, data_root, width, height, repeats, flip_p=0.5, placeholder_token="*", model=None, cond_model=None, device=None, template_file=None, include_cond=False, batch_size=1, gradient_step=1, shuffle_tags=False, tag_drop_out=0, latent_sampling_method='once'): re_word = re.compile(shared.opts.dataset_filename_word_regex) if len(shared.opts.dataset_filename_word_regex) > 0 else None - + self.placeholder_token = placeholder_token self.width = width @@ -50,14 +50,14 @@ class PersonalizedBase(Dataset): self.image_paths = [os.path.join(data_root, file_path) for file_path in os.listdir(data_root)] - + self.shuffle_tags = shuffle_tags self.tag_drop_out = tag_drop_out print("Preparing dataset...") for path in tqdm.tqdm(self.image_paths): if shared.state.interrupted: - raise Exception("inturrupted") + raise Exception("interrupted") try: image = Image.open(path).convert('RGB').resize((self.width, self.height), PIL.Image.BICUBIC) except Exception: @@ -144,7 +144,7 @@ class PersonalizedDataLoader(DataLoader): self.collate_fn = collate_wrapper_random else: self.collate_fn = collate_wrapper - + class BatchLoader: def __init__(self, data): diff --git a/modules/textual_inversion/textual_inversion.py b/modules/textual_inversion/textual_inversion.py index e28c357a..daf3997b 100644 --- a/modules/textual_inversion/textual_inversion.py +++ b/modules/textual_inversion/textual_inversion.py @@ -133,7 +133,7 @@ class EmbeddingDatabase: process_file(fullfn, fn) except Exception: - print(f"Error loading emedding {fn}:", file=sys.stderr) + print(f"Error loading embedding {fn}:", file=sys.stderr) print(traceback.format_exc(), file=sys.stderr) continue @@ -194,7 +194,7 @@ def write_loss(log_directory, filename, step, epoch_len, values): csv_writer.writeheader() epoch = (step - 1) // epoch_len - epoch_step = (step - 1) % epoch_len + epoch_step = (step - 1) % epoch_len csv_writer.writerow({ "step": step, @@ -270,9 +270,9 @@ def train_embedding(embedding_name, learn_rate, batch_size, gradient_step, data_ # dataset loading may take a while, so input validations and early returns should be done before this shared.state.textinfo = f"Preparing dataset from {html.escape(data_root)}..." old_parallel_processing_allowed = shared.parallel_processing_allowed - + pin_memory = shared.opts.pin_memory - + ds = modules.textual_inversion.dataset.PersonalizedBase(data_root=data_root, width=training_width, height=training_height, repeats=shared.opts.training_image_repeats_per_epoch, placeholder_token=embedding_name, model=shared.sd_model, cond_model=shared.sd_model.cond_stage_model, device=devices.device, template_file=template_file, batch_size=batch_size, gradient_step=gradient_step, shuffle_tags=shuffle_tags, tag_drop_out=tag_drop_out, latent_sampling_method=latent_sampling_method) latent_sampling_method = ds.latent_sampling_method @@ -295,12 +295,12 @@ def train_embedding(embedding_name, learn_rate, batch_size, gradient_step, data_ loss_step = 0 _loss_step = 0 #internal - + last_saved_file = "" last_saved_image = "" forced_filename = "" embedding_yet_to_be_embedded = False - + pbar = tqdm.tqdm(total=steps - initial_step) try: for i in range((steps-initial_step) * gradient_step): @@ -327,10 +327,10 @@ def train_embedding(embedding_name, learn_rate, batch_size, gradient_step, data_ c = shared.sd_model.cond_stage_model(batch.cond_text) loss = shared.sd_model(x, c)[0] / gradient_step del x - + _loss_step += loss.item() scaler.scale(loss).backward() - + # go back until we reach gradient accumulation steps if (j + 1) % gradient_step != 0: continue diff --git a/scripts/prompt_matrix.py b/scripts/prompt_matrix.py index c53ca28c..4c79eaef 100644 --- a/scripts/prompt_matrix.py +++ b/scripts/prompt_matrix.py @@ -18,7 +18,7 @@ def draw_xy_grid(xs, ys, x_label, y_label, cell): ver_texts = [[images.GridAnnotation(y_label(y))] for y in ys] hor_texts = [[images.GridAnnotation(x_label(x))] for x in xs] - first_pocessed = None + first_processed = None state.job_count = len(xs) * len(ys) @@ -27,17 +27,17 @@ def draw_xy_grid(xs, ys, x_label, y_label, cell): state.job = f"{ix + iy * len(xs) + 1} out of {len(xs) * len(ys)}" processed = cell(x, y) - if first_pocessed is None: - first_pocessed = processed + if first_processed is None: + first_processed = processed res.append(processed.images[0]) grid = images.image_grid(res, rows=len(ys)) grid = images.draw_grid_annotations(grid, res[0].width, res[0].height, hor_texts, ver_texts) - first_pocessed.images = [grid] + first_processed.images = [grid] - return first_pocessed + return first_processed class Script(scripts.Script): diff --git a/webui.py b/webui.py index c2d0c6be..4b32e77d 100644 --- a/webui.py +++ b/webui.py @@ -153,8 +153,8 @@ def webui(): # gradio uses a very open CORS policy via app.user_middleware, which makes it possible for # an attacker to trick the user into opening a malicious HTML page, which makes a request to the - # running web ui and do whatever the attcker wants, including installing an extension and - # runnnig its code. We disable this here. Suggested by RyotaK. + # running web ui and do whatever the attacker wants, including installing an extension and + # running its code. We disable this here. Suggested by RyotaK. app.user_middleware = [x for x in app.user_middleware if x.cls.__name__ != 'CORSMiddleware'] setup_cors(app) From 35e1017e3ea0a3ad9ec28c9b447200a70a65c0ae Mon Sep 17 00:00:00 2001 From: Akiba Date: Fri, 16 Dec 2022 20:43:09 +0800 Subject: [PATCH 25/59] fix: xformers --- modules/import_hook.py | 18 ++++++++++++++++++ webui.py | 1 + 2 files changed, 19 insertions(+) create mode 100644 modules/import_hook.py diff --git a/modules/import_hook.py b/modules/import_hook.py new file mode 100644 index 00000000..eb10e4fd --- /dev/null +++ b/modules/import_hook.py @@ -0,0 +1,18 @@ +import builtins +import sys + +old_import = builtins.__import__ +IMPORT_BLACKLIST = [] + + +if "xformers" not in "".join(sys.argv): + IMPORT_BLACKLIST.append("xformers") + + +def import_hook(*args, **kwargs): + if args[0] in IMPORT_BLACKLIST: + raise ImportError("Import of %s is blacklisted" % args[0]) + return old_import(*args, **kwargs) + + +builtins.__import__ = import_hook diff --git a/webui.py b/webui.py index c2d0c6be..18ee5a3d 100644 --- a/webui.py +++ b/webui.py @@ -8,6 +8,7 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.gzip import GZipMiddleware +from modules import import_hook from modules.call_queue import wrap_queued_call, queue_lock, wrap_gradio_gpu_call from modules.paths import script_path From 8b0703b8fcdab153958b11f0dd5e5b6b58565fed Mon Sep 17 00:00:00 2001 From: "Alex \"mcmonkey\" Goodwin" Date: Fri, 16 Dec 2022 08:18:29 -0800 Subject: [PATCH 26/59] Add a workaround patch for DPM2 a issue DPM2 a and DPM2 a Karras samplers are both affected by an issue described by https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/3483 and can be resolved by a workaround suggested by the k-diffusion author at https://github.com/crowsonkb/k-diffusion/issues/43#issuecomment-1305195666 --- modules/sd_samplers.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/sd_samplers.py b/modules/sd_samplers.py index 4c123d3b..b8e0ce53 100644 --- a/modules/sd_samplers.py +++ b/modules/sd_samplers.py @@ -494,6 +494,9 @@ class KDiffusionSampler: x = x * sigmas[0] + if self.funcname == "sample_dpm_2_ancestral": # workaround dpm2 a issue + sigmas = torch.cat([sigmas[:-2], sigmas[-1:]]) + extra_params_kwargs = self.initialize(p) if 'sigma_min' in inspect.signature(self.func).parameters: extra_params_kwargs['sigma_min'] = self.model_wrap.sigmas[0].item() From 180fdf7809ea18de2d3b04618846d5a4e33c002e Mon Sep 17 00:00:00 2001 From: "Alex \"mcmonkey\" Goodwin" Date: Fri, 16 Dec 2022 08:42:00 -0800 Subject: [PATCH 27/59] apply to DPM2 (non-ancestral) as well --- modules/sd_samplers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/sd_samplers.py b/modules/sd_samplers.py index b8e0ce53..ae3d8bfa 100644 --- a/modules/sd_samplers.py +++ b/modules/sd_samplers.py @@ -494,7 +494,7 @@ class KDiffusionSampler: x = x * sigmas[0] - if self.funcname == "sample_dpm_2_ancestral": # workaround dpm2 a issue + if self.funcname in ['sample_dpm_2_ancestral', 'sample_dpm_2']: sigmas = torch.cat([sigmas[:-2], sigmas[-1:]]) extra_params_kwargs = self.initialize(p) From b7c478c3ebb2b1844efd5d6bddb69095dd10808f Mon Sep 17 00:00:00 2001 From: MMaker Date: Sat, 17 Dec 2022 00:45:43 -0500 Subject: [PATCH 28/59] fix: Modify font size when unable to fit in plot This prevents scenarios where text without line breaks will start overlapping with each other when generating X/Y plots. This is most evident when generating X/Y plots with checkpoints, as most don't contain spaces and sometimes include extra information such as the epoch, making it extra long. --- modules/images.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/modules/images.py b/modules/images.py index 8146f580..ad97980c 100644 --- a/modules/images.py +++ b/modules/images.py @@ -136,8 +136,19 @@ def draw_grid_annotations(im, width, height, hor_texts, ver_texts): lines.append(word) return lines - def draw_texts(drawing, draw_x, draw_y, lines): + def get_font(fontsize): + try: + return ImageFont.truetype(opts.font or Roboto, fontsize) + except Exception: + return ImageFont.truetype(Roboto, fontsize) + + def draw_texts(drawing, draw_x, draw_y, lines, initial_fnt, initial_fontsize): for i, line in enumerate(lines): + fnt = initial_fnt + fontsize = initial_fontsize + while drawing.multiline_textsize(line.text, font=fnt)[0] > line.allowed_width and fontsize > 0: + fontsize -= 1 + fnt = get_font(fontsize) drawing.multiline_text((draw_x, draw_y + line.size[1] / 2), line.text, font=fnt, fill=color_active if line.is_active else color_inactive, anchor="mm", align="center") if not line.is_active: @@ -148,10 +159,7 @@ def draw_grid_annotations(im, width, height, hor_texts, ver_texts): fontsize = (width + height) // 25 line_spacing = fontsize // 2 - try: - fnt = ImageFont.truetype(opts.font or Roboto, fontsize) - except Exception: - fnt = ImageFont.truetype(Roboto, fontsize) + fnt = get_font(fontsize) color_active = (0, 0, 0) color_inactive = (153, 153, 153) @@ -178,6 +186,7 @@ def draw_grid_annotations(im, width, height, hor_texts, ver_texts): for line in texts: bbox = calc_d.multiline_textbbox((0, 0), line.text, font=fnt) line.size = (bbox[2] - bbox[0], bbox[3] - bbox[1]) + line.allowed_width = allowed_width hor_text_heights = [sum([line.size[1] + line_spacing for line in lines]) - line_spacing for lines in hor_texts] ver_text_heights = [sum([line.size[1] + line_spacing for line in lines]) - line_spacing * len(lines) for lines in @@ -194,13 +203,13 @@ def draw_grid_annotations(im, width, height, hor_texts, ver_texts): x = pad_left + width * col + width / 2 y = pad_top / 2 - hor_text_heights[col] / 2 - draw_texts(d, x, y, hor_texts[col]) + draw_texts(d, x, y, hor_texts[col], fnt, fontsize) for row in range(rows): x = pad_left / 2 y = pad_top + height * row + height / 2 - ver_text_heights[row] / 2 - draw_texts(d, x, y, ver_texts[row]) + draw_texts(d, x, y, ver_texts[row], fnt, fontsize) return result From 16b4509fa60ec03102b2452b41799dafccd35970 Mon Sep 17 00:00:00 2001 From: brkirch Date: Sat, 17 Dec 2022 03:21:19 -0500 Subject: [PATCH 29/59] Add numpy fix for MPS on PyTorch 1.12.1 When saving training results with torch.save(), an exception is thrown: "RuntimeError: Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead." So for MPS, check if Tensor.requires_grad and detach() if necessary. --- modules/devices.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/modules/devices.py b/modules/devices.py index f8cffae1..800510b7 100644 --- a/modules/devices.py +++ b/modules/devices.py @@ -125,7 +125,16 @@ def layer_norm_fix(*args, **kwargs): return orig_layer_norm(*args, **kwargs) +# MPS workaround for https://github.com/pytorch/pytorch/issues/90532 +orig_tensor_numpy = torch.Tensor.numpy +def numpy_fix(self, *args, **kwargs): + if self.requires_grad: + self = self.detach() + return orig_tensor_numpy(self, *args, **kwargs) + + # PyTorch 1.13 doesn't need these fixes but unfortunately is slower and has regressions that prevent training from working if has_mps() and version.parse(torch.__version__) < version.parse("1.13"): torch.Tensor.to = tensor_to_fix torch.nn.functional.layer_norm = layer_norm_fix + torch.Tensor.numpy = numpy_fix From cca16373def60bfc6d159a3c2dca91d0ba48112a Mon Sep 17 00:00:00 2001 From: brkirch Date: Sat, 17 Dec 2022 03:24:54 -0500 Subject: [PATCH 30/59] Add attributes used by MPS --- modules/safe.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/safe.py b/modules/safe.py index 10460ad0..7c89c4c2 100644 --- a/modules/safe.py +++ b/modules/safe.py @@ -37,16 +37,16 @@ class RestrictedUnpickler(pickle.Unpickler): if module == 'collections' and name == 'OrderedDict': return getattr(collections, name) - if module == 'torch._utils' and name in ['_rebuild_tensor_v2', '_rebuild_parameter']: + if module == 'torch._utils' and name in ['_rebuild_tensor_v2', '_rebuild_parameter', '_rebuild_device_tensor_from_numpy']: return getattr(torch._utils, name) - if module == 'torch' and name in ['FloatStorage', 'HalfStorage', 'IntStorage', 'LongStorage', 'DoubleStorage', 'ByteStorage']: + if module == 'torch' and name in ['FloatStorage', 'HalfStorage', 'IntStorage', 'LongStorage', 'DoubleStorage', 'ByteStorage', 'float32']: return getattr(torch, name) if module == 'torch.nn.modules.container' and name in ['ParameterDict']: return getattr(torch.nn.modules.container, name) - if module == 'numpy.core.multiarray' and name == 'scalar': - return numpy.core.multiarray.scalar - if module == 'numpy' and name == 'dtype': - return numpy.dtype + if module == 'numpy.core.multiarray' and name in ['scalar', '_reconstruct']: + return getattr(numpy.core.multiarray, name) + if module == 'numpy' and name in ['dtype', 'ndarray']: + return getattr(numpy, name) if module == '_codecs' and name == 'encode': return encode if module == "pytorch_lightning.callbacks" and name == 'model_checkpoint': From a26fe85056cf0dacef2d78cccf6ab100fd16da1c Mon Sep 17 00:00:00 2001 From: timntorres Date: Sat, 17 Dec 2022 04:31:03 -0800 Subject: [PATCH 31/59] Add upscaler name as a suffix. --- modules/extras.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/extras.py b/modules/extras.py index bc349d5e..9b60e360 100644 --- a/modules/extras.py +++ b/modules/extras.py @@ -193,8 +193,13 @@ def run_extras(extras_mode, resize_mode, image, image_folder, input_dir, output_ else: basename = '' + # Add upscaler name as a suffix. + suffix = f"-{shared.sd_upscalers[extras_upscaler_1].name}" + if extras_upscaler_2 and extras_upscaler_2_visibility: + suffix += f"-{shared.sd_upscalers[extras_upscaler_2].name}" + images.save_image(image, path=outpath, basename=basename, seed=None, prompt=None, extension=opts.samples_format, info=info, short_filename=True, - no_prompt=True, grid=False, pnginfo_section_name="extras", existing_info=existing_pnginfo, forced_filename=None) + no_prompt=True, grid=False, pnginfo_section_name="extras", existing_info=existing_pnginfo, forced_filename=None, suffix=suffix) if opts.enable_pnginfo: image.info = existing_pnginfo From a7a039d53a69f8c32cb889fe322e769b238fec27 Mon Sep 17 00:00:00 2001 From: timntorres Date: Sat, 17 Dec 2022 06:28:51 -0800 Subject: [PATCH 32/59] Add option to include upscaler name in filename. --- modules/extras.py | 5 +++-- modules/shared.py | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/extras.py b/modules/extras.py index 9b60e360..074a7c22 100644 --- a/modules/extras.py +++ b/modules/extras.py @@ -194,8 +194,9 @@ def run_extras(extras_mode, resize_mode, image, image_folder, input_dir, output_ basename = '' # Add upscaler name as a suffix. - suffix = f"-{shared.sd_upscalers[extras_upscaler_1].name}" - if extras_upscaler_2 and extras_upscaler_2_visibility: + suffix = f"-{shared.sd_upscalers[extras_upscaler_1].name}" if shared.opts.use_upscaler_name_as_suffix else "" + # Add second upscaler if applicable. + if suffix and extras_upscaler_2 and extras_upscaler_2_visibility: suffix += f"-{shared.sd_upscalers[extras_upscaler_2].name}" images.save_image(image, path=outpath, basename=basename, seed=None, prompt=None, extension=opts.samples_format, info=info, short_filename=True, diff --git a/modules/shared.py b/modules/shared.py index dc45fcaa..218894e8 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -293,6 +293,7 @@ options_templates.update(options_section(('saving-images', "Saving images/grids" "export_for_4chan": OptionInfo(True, "If PNG image is larger than 4MB or any dimension is larger than 4000, downscale and save copy as JPG"), "use_original_name_batch": OptionInfo(False, "Use original name for output filename during batch process in extras tab"), + "use_upscaler_name_as_suffix": OptionInfo(False, "Add upscaler name to the end of filename in the extras tab"), "save_selected_only": OptionInfo(True, "When using 'Save' button, only save a single selected image"), "do_not_add_watermark": OptionInfo(False, "Do not add watermark to images"), From 6fd91c9179f51dd2f73f03eeabd12bfd081941c5 Mon Sep 17 00:00:00 2001 From: timntorres Date: Sat, 17 Dec 2022 08:59:02 -0800 Subject: [PATCH 33/59] Update OptionInfo to match preexisting option. --- modules/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/shared.py b/modules/shared.py index 218894e8..230c377e 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -293,7 +293,7 @@ options_templates.update(options_section(('saving-images', "Saving images/grids" "export_for_4chan": OptionInfo(True, "If PNG image is larger than 4MB or any dimension is larger than 4000, downscale and save copy as JPG"), "use_original_name_batch": OptionInfo(False, "Use original name for output filename during batch process in extras tab"), - "use_upscaler_name_as_suffix": OptionInfo(False, "Add upscaler name to the end of filename in the extras tab"), + "use_upscaler_name_as_suffix": OptionInfo(False, "Use upscaler name as filename suffix in the extras tab"), "save_selected_only": OptionInfo(True, "When using 'Save' button, only save a single selected image"), "do_not_add_watermark": OptionInfo(False, "Do not add watermark to images"), From 35f0698ae85571f688e82447c989b75e4fe81b0d Mon Sep 17 00:00:00 2001 From: Billy Cao Date: Sun, 18 Dec 2022 20:45:30 +0800 Subject: [PATCH 34/59] Dirty fix for missing PIL supported file extensions --- launch.py | 1 + 1 file changed, 1 insertion(+) diff --git a/launch.py b/launch.py index ad9ddd5a..581a21ff 100644 --- a/launch.py +++ b/launch.py @@ -7,6 +7,7 @@ import shlex import platform import argparse import json +from PIL import Image dir_repos = "repositories" dir_extensions = "extensions" From c02ef0f4286c618d30ee028778f58ca7809c7d93 Mon Sep 17 00:00:00 2001 From: Billy Cao Date: Sun, 18 Dec 2022 20:51:59 +0800 Subject: [PATCH 35/59] Fix PIL being imported before its installed (for new users only) --- launch.py | 1 - modules/shared.py | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/launch.py b/launch.py index 581a21ff..ad9ddd5a 100644 --- a/launch.py +++ b/launch.py @@ -7,7 +7,6 @@ import shlex import platform import argparse import json -from PIL import Image dir_repos = "repositories" dir_extensions = "extensions" diff --git a/modules/shared.py b/modules/shared.py index c36ee211..734ea2fe 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -5,6 +5,7 @@ import os import sys import time +from PIL import Image import gradio as gr import tqdm From 492052b5df657c3280f433fec047667246694bdb Mon Sep 17 00:00:00 2001 From: MMaker Date: Sun, 18 Dec 2022 10:47:02 -0500 Subject: [PATCH 36/59] feat: Add upscale latent, VAE, styles to X/Y plot Adds upscale latent space for hires., VAE, and Styles as new axis options to the X/Y plot. --- scripts/xy_grid.py | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/scripts/xy_grid.py b/scripts/xy_grid.py index d402c281..3e0b2805 100644 --- a/scripts/xy_grid.py +++ b/scripts/xy_grid.py @@ -10,13 +10,16 @@ import numpy as np import modules.scripts as scripts import gradio as gr -from modules import images, sd_samplers +from modules import images, paths, sd_samplers from modules.hypernetworks import hypernetwork from modules.processing import process_images, Processed, StableDiffusionProcessingTxt2Img from modules.shared import opts, cmd_opts, state import modules.shared as shared import modules.sd_samplers import modules.sd_models +import modules.sd_vae +import glob +import os import re @@ -114,6 +117,38 @@ def apply_clip_skip(p, x, xs): opts.data["CLIP_stop_at_last_layers"] = x +def apply_upscale_latent_space(p, x, xs): + if x.lower().strip() != '0': + opts.data["use_scale_latent_for_hires_fix"] = True + else: + opts.data["use_scale_latent_for_hires_fix"] = False + + +def find_vae(name: str): + if name.lower() in ['auto', 'none']: + return name + else: + vae_path = os.path.abspath(os.path.join(paths.models_path, 'VAE')) + found = glob.glob(os.path.join(vae_path, f'**/{name}.*pt'), recursive=True) + if found: + return found[0] + else: + return 'auto' + + +def apply_vae(p, x, xs): + if x.lower().strip() == 'none': + modules.sd_vae.reload_vae_weights(shared.sd_model, vae_file='None') + else: + found = find_vae(x) + if found: + v = modules.sd_vae.reload_vae_weights(shared.sd_model, vae_file=found) + + +def apply_styles(p: StableDiffusionProcessingTxt2Img, x: str, _): + p.styles = x.split(',') + + def format_value_add_label(p, opt, x): if type(x) == float: x = round(x, 8) @@ -167,7 +202,10 @@ axis_options = [ AxisOption("Eta", float, apply_field("eta"), format_value_add_label, None), AxisOption("Clip skip", int, apply_clip_skip, format_value_add_label, None), AxisOption("Denoising", float, apply_field("denoising_strength"), format_value_add_label, None), + AxisOption("Upscale latent space for hires.", str, apply_upscale_latent_space, format_value_add_label, None), AxisOption("Cond. Image Mask Weight", float, apply_field("inpainting_mask_weight"), format_value_add_label, None), + AxisOption("VAE", str, apply_vae, format_value_add_label, None), + AxisOption("Styles", str, apply_styles, format_value_add_label, None), ] @@ -229,14 +267,18 @@ class SharedSettingsStackHelper(object): self.CLIP_stop_at_last_layers = opts.CLIP_stop_at_last_layers self.hypernetwork = opts.sd_hypernetwork self.model = shared.sd_model + self.use_scale_latent_for_hires_fix = opts.use_scale_latent_for_hires_fix + self.vae = opts.sd_vae def __exit__(self, exc_type, exc_value, tb): modules.sd_models.reload_model_weights(self.model) + modules.sd_vae.reload_vae_weights(self.model, vae_file=find_vae(self.vae)) hypernetwork.load_hypernetwork(self.hypernetwork) hypernetwork.apply_strength() opts.data["CLIP_stop_at_last_layers"] = self.CLIP_stop_at_last_layers + opts.data["use_scale_latent_for_hires_fix"] = self.use_scale_latent_for_hires_fix re_range = re.compile(r"\s*([+-]?\s*\d+)\s*-\s*([+-]?\s*\d+)(?:\s*\(([+-]\d+)\s*\))?\s*") From 7ba9bc2fdbfae8115294962510492faafeb48573 Mon Sep 17 00:00:00 2001 From: "Alex \"mcmonkey\" Goodwin" Date: Sun, 18 Dec 2022 19:16:42 -0800 Subject: [PATCH 37/59] fix dpm2 in img2img as well --- modules/sd_samplers.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/sd_samplers.py b/modules/sd_samplers.py index ae3d8bfa..1a1b8919 100644 --- a/modules/sd_samplers.py +++ b/modules/sd_samplers.py @@ -454,6 +454,9 @@ class KDiffusionSampler: else: sigmas = self.model_wrap.get_sigmas(steps) + if self.funcname in ['sample_dpm_2_ancestral', 'sample_dpm_2']: + sigmas = torch.cat([sigmas[:-2], sigmas[-1:]]) + sigma_sched = sigmas[steps - t_enc - 1:] xi = x + noise * sigma_sched[0] From 22f1527fa79a03dbc8b1a4eec3b22369a877f4bd Mon Sep 17 00:00:00 2001 From: Philpax Date: Tue, 20 Dec 2022 20:36:49 +1100 Subject: [PATCH 38/59] feat(api): add override_settings_restore_afterwards --- modules/processing.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/modules/processing.py b/modules/processing.py index 24c537d1..f7335da2 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -77,7 +77,7 @@ class StableDiffusionProcessing(): """ The first set of paramaters: sd_models -> do_not_reload_embeddings represent the minimum required to create a StableDiffusionProcessing """ - def __init__(self, sd_model=None, outpath_samples=None, outpath_grids=None, prompt: str = "", styles: List[str] = None, seed: int = -1, subseed: int = -1, subseed_strength: float = 0, seed_resize_from_h: int = -1, seed_resize_from_w: int = -1, seed_enable_extras: bool = True, sampler_name: str = None, batch_size: int = 1, n_iter: int = 1, steps: int = 50, cfg_scale: float = 7.0, width: int = 512, height: int = 512, restore_faces: bool = False, tiling: bool = False, do_not_save_samples: bool = False, do_not_save_grid: bool = False, extra_generation_params: Dict[Any, Any] = None, overlay_images: Any = None, negative_prompt: str = None, eta: float = None, do_not_reload_embeddings: bool = False, denoising_strength: float = 0, ddim_discretize: str = None, s_churn: float = 0.0, s_tmax: float = None, s_tmin: float = 0.0, s_noise: float = 1.0, override_settings: Dict[str, Any] = None, sampler_index: int = None): + def __init__(self, sd_model=None, outpath_samples=None, outpath_grids=None, prompt: str = "", styles: List[str] = None, seed: int = -1, subseed: int = -1, subseed_strength: float = 0, seed_resize_from_h: int = -1, seed_resize_from_w: int = -1, seed_enable_extras: bool = True, sampler_name: str = None, batch_size: int = 1, n_iter: int = 1, steps: int = 50, cfg_scale: float = 7.0, width: int = 512, height: int = 512, restore_faces: bool = False, tiling: bool = False, do_not_save_samples: bool = False, do_not_save_grid: bool = False, extra_generation_params: Dict[Any, Any] = None, overlay_images: Any = None, negative_prompt: str = None, eta: float = None, do_not_reload_embeddings: bool = False, denoising_strength: float = 0, ddim_discretize: str = None, s_churn: float = 0.0, s_tmax: float = None, s_tmin: float = 0.0, s_noise: float = 1.0, override_settings: Dict[str, Any] = None, override_settings_restore_afterwards: bool = True, sampler_index: int = None): if sampler_index is not None: print("sampler_index argument for StableDiffusionProcessing does not do anything; use sampler_name", file=sys.stderr) @@ -118,6 +118,7 @@ class StableDiffusionProcessing(): self.s_tmax = s_tmax or float('inf') # not representable as a standard ui option self.s_noise = s_noise or opts.s_noise self.override_settings = {k: v for k, v in (override_settings or {}).items() if k not in shared.restricted_opts} + self.override_settings_restore_afterwards = override_settings_restore_afterwards self.is_using_inpainting_conditioning = False if not seed_enable_extras: @@ -147,11 +148,11 @@ class StableDiffusionProcessing(): # The "masked-image" in this case will just be all zeros since the entire image is masked. image_conditioning = torch.zeros(x.shape[0], 3, height, width, device=x.device) - image_conditioning = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(image_conditioning)) + image_conditioning = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(image_conditioning)) # Add the fake full 1s mask to the first dimension. image_conditioning = torch.nn.functional.pad(image_conditioning, (0, 0, 0, 0, 1, 0), value=1.0) - image_conditioning = image_conditioning.to(x.dtype) + image_conditioning = image_conditioning.to(x.dtype) return image_conditioning @@ -199,7 +200,7 @@ class StableDiffusionProcessing(): source_image * (1.0 - conditioning_mask), getattr(self, "inpainting_mask_weight", shared.opts.inpainting_mask_weight) ) - + # Encode the new masked image using first stage of network. conditioning_image = self.sd_model.get_first_stage_encoding(self.sd_model.encode_first_stage(conditioning_image)) @@ -463,12 +464,14 @@ def process_images(p: StableDiffusionProcessing) -> Processed: res = process_images_inner(p) - finally: # restore opts to original state - for k, v in stored_opts.items(): - setattr(opts, k, v) - if k == 'sd_hypernetwork': shared.reload_hypernetworks() - if k == 'sd_model_checkpoint': sd_models.reload_model_weights() - if k == 'sd_vae': sd_vae.reload_vae_weights() + finally: + # restore opts to original state + if p.override_settings_restore_afterwards: + for k, v in stored_opts.items(): + setattr(opts, k, v) + if k == 'sd_hypernetwork': shared.reload_hypernetworks() + if k == 'sd_model_checkpoint': sd_models.reload_model_weights() + if k == 'sd_vae': sd_vae.reload_vae_weights() return res @@ -537,7 +540,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: for n in range(p.n_iter): if state.skipped: state.skipped = False - + if state.interrupted: break @@ -612,7 +615,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: image.info["parameters"] = text output_images.append(image) - del x_samples_ddim + del x_samples_ddim devices.torch_gc() @@ -720,7 +723,7 @@ class StableDiffusionProcessingTxt2Img(StableDiffusionProcessing): samples = torch.nn.functional.interpolate(samples, size=(self.height // opt_f, self.width // opt_f), mode="bilinear") - # Avoid making the inpainting conditioning unless necessary as + # Avoid making the inpainting conditioning unless necessary as # this does need some extra compute to decode / encode the image again. if getattr(self, "inpainting_mask_weight", shared.opts.inpainting_mask_weight) < 1.0: image_conditioning = self.img2img_image_conditioning(decode_first_stage(self.sd_model, samples), samples) From d050bb78636236338440768f871a0f2bb9f277f6 Mon Sep 17 00:00:00 2001 From: Florian Chrometz <1118875+florianchrometz@users.noreply.github.com> Date: Tue, 20 Dec 2022 20:19:50 +0100 Subject: [PATCH 39/59] Set httpcore version in requirements - fixes #4833 --- requirements_versions.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements_versions.txt b/requirements_versions.txt index 185cd066..7208aeba 100644 --- a/requirements_versions.txt +++ b/requirements_versions.txt @@ -26,3 +26,4 @@ inflection==0.5.1 GitPython==3.1.27 torchsde==0.2.5 safetensors==0.2.5 +httpcore<=0.15 From 35b1775b32a07f1b7c9dccad61f7aa77027a00fa Mon Sep 17 00:00:00 2001 From: brkirch Date: Mon, 19 Dec 2022 17:25:14 -0500 Subject: [PATCH 40/59] Use other MPS optimization for large q.shape[0] * q.shape[1] Check if q.shape[0] * q.shape[1] is 2**18 or larger and use the lower memory usage MPS optimization if it is. This should prevent most crashes that were occurring at certain resolutions (e.g. 1024x1024, 2048x512, 512x2048). Also included is a change to check slice_size and prevent it from being divisible by 4096 which also results in a crash. Otherwise a crash can occur at 1024x512 or 512x1024 resolution. --- modules/sd_hijack_optimizations.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/modules/sd_hijack_optimizations.py b/modules/sd_hijack_optimizations.py index 98123fbf..02c87f40 100644 --- a/modules/sd_hijack_optimizations.py +++ b/modules/sd_hijack_optimizations.py @@ -127,7 +127,7 @@ def check_for_psutil(): invokeAI_mps_available = check_for_psutil() -# -- Taken from https://github.com/invoke-ai/InvokeAI -- +# -- Taken from https://github.com/invoke-ai/InvokeAI and modified -- if invokeAI_mps_available: import psutil mem_total_gb = psutil.virtual_memory().total // (1 << 30) @@ -152,14 +152,16 @@ def einsum_op_slice_1(q, k, v, slice_size): return r def einsum_op_mps_v1(q, k, v): - if q.shape[1] <= 4096: # (512x512) max q.shape[1]: 4096 + if q.shape[0] * q.shape[1] <= 2**16: # (512x512) max q.shape[1]: 4096 return einsum_op_compvis(q, k, v) else: slice_size = math.floor(2**30 / (q.shape[0] * q.shape[1])) + if slice_size % 4096 == 0: + slice_size -= 1 return einsum_op_slice_1(q, k, v, slice_size) def einsum_op_mps_v2(q, k, v): - if mem_total_gb > 8 and q.shape[1] <= 4096: + if mem_total_gb > 8 and q.shape[0] * q.shape[1] <= 2**16: return einsum_op_compvis(q, k, v) else: return einsum_op_slice_0(q, k, v, 1) @@ -188,7 +190,7 @@ def einsum_op(q, k, v): return einsum_op_cuda(q, k, v) if q.device.type == 'mps': - if mem_total_gb >= 32: + if mem_total_gb >= 32 and q.shape[0] % 32 != 0 and q.shape[0] * q.shape[1] < 2**18: return einsum_op_mps_v1(q, k, v) return einsum_op_mps_v2(q, k, v) From 13e0295ab682299e3280eb6ff28be0870f2bc57c Mon Sep 17 00:00:00 2001 From: Akiba Date: Sat, 24 Dec 2022 11:17:21 +0800 Subject: [PATCH 41/59] fix: xformers use importlib --- modules/import_hook.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/modules/import_hook.py b/modules/import_hook.py index eb10e4fd..7403135d 100644 --- a/modules/import_hook.py +++ b/modules/import_hook.py @@ -1,18 +1,5 @@ -import builtins import sys -old_import = builtins.__import__ -IMPORT_BLACKLIST = [] - if "xformers" not in "".join(sys.argv): - IMPORT_BLACKLIST.append("xformers") - - -def import_hook(*args, **kwargs): - if args[0] in IMPORT_BLACKLIST: - raise ImportError("Import of %s is blacklisted" % args[0]) - return old_import(*args, **kwargs) - - -builtins.__import__ = import_hook + sys.modules["xformers"] = None From 0c747d4013f41f6c887a63d256af884aa8872f91 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 24 Dec 2022 07:57:56 +0300 Subject: [PATCH 42/59] add a comment for disable xformers hack --- modules/import_hook.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/import_hook.py b/modules/import_hook.py index 7403135d..28c67dfa 100644 --- a/modules/import_hook.py +++ b/modules/import_hook.py @@ -1,5 +1,5 @@ import sys - -if "xformers" not in "".join(sys.argv): +# this will break any attempt to import xformers which will prevent stability diffusion repo from trying to use it +if "--xformers" not in "".join(sys.argv): sys.modules["xformers"] = None From 399b229783a7b5fddab0a258740b4d59d668ee12 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 24 Dec 2022 09:03:45 +0300 Subject: [PATCH 43/59] eliminate duplicated code add an option to samplers for skipping next to last sigma --- modules/sd_samplers.py | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/modules/sd_samplers.py b/modules/sd_samplers.py index 1a1b8919..d26e48dc 100644 --- a/modules/sd_samplers.py +++ b/modules/sd_samplers.py @@ -23,16 +23,16 @@ samplers_k_diffusion = [ ('Euler', 'sample_euler', ['k_euler'], {}), ('LMS', 'sample_lms', ['k_lms'], {}), ('Heun', 'sample_heun', ['k_heun'], {}), - ('DPM2', 'sample_dpm_2', ['k_dpm_2'], {}), - ('DPM2 a', 'sample_dpm_2_ancestral', ['k_dpm_2_a'], {}), + ('DPM2', 'sample_dpm_2', ['k_dpm_2'], {'discard_next_to_last_sigma': True}), + ('DPM2 a', 'sample_dpm_2_ancestral', ['k_dpm_2_a'], {'discard_next_to_last_sigma': True}), ('DPM++ 2S a', 'sample_dpmpp_2s_ancestral', ['k_dpmpp_2s_a'], {}), ('DPM++ 2M', 'sample_dpmpp_2m', ['k_dpmpp_2m'], {}), ('DPM++ SDE', 'sample_dpmpp_sde', ['k_dpmpp_sde'], {}), ('DPM fast', 'sample_dpm_fast', ['k_dpm_fast'], {}), ('DPM adaptive', 'sample_dpm_adaptive', ['k_dpm_ad'], {}), ('LMS Karras', 'sample_lms', ['k_lms_ka'], {'scheduler': 'karras'}), - ('DPM2 Karras', 'sample_dpm_2', ['k_dpm_2_ka'], {'scheduler': 'karras'}), - ('DPM2 a Karras', 'sample_dpm_2_ancestral', ['k_dpm_2_a_ka'], {'scheduler': 'karras'}), + ('DPM2 Karras', 'sample_dpm_2', ['k_dpm_2_ka'], {'scheduler': 'karras', 'discard_next_to_last_sigma': True}), + ('DPM2 a Karras', 'sample_dpm_2_ancestral', ['k_dpm_2_a_ka'], {'scheduler': 'karras', 'discard_next_to_last_sigma': True}), ('DPM++ 2S a Karras', 'sample_dpmpp_2s_ancestral', ['k_dpmpp_2s_a_ka'], {'scheduler': 'karras'}), ('DPM++ 2M Karras', 'sample_dpmpp_2m', ['k_dpmpp_2m_ka'], {'scheduler': 'karras'}), ('DPM++ SDE Karras', 'sample_dpmpp_sde', ['k_dpmpp_sde_ka'], {'scheduler': 'karras'}), @@ -444,9 +444,7 @@ class KDiffusionSampler: return extra_params_kwargs - def sample_img2img(self, p, x, noise, conditioning, unconditional_conditioning, steps=None, image_conditioning=None): - steps, t_enc = setup_img2img_steps(p, steps) - + def get_sigmas(self, p, steps): if p.sampler_noise_scheduler_override: sigmas = p.sampler_noise_scheduler_override(steps) elif self.config is not None and self.config.options.get('scheduler', None) == 'karras': @@ -454,9 +452,16 @@ class KDiffusionSampler: else: sigmas = self.model_wrap.get_sigmas(steps) - if self.funcname in ['sample_dpm_2_ancestral', 'sample_dpm_2']: + if self.config is not None and self.config.options.get('discard_next_to_last_sigma', False): sigmas = torch.cat([sigmas[:-2], sigmas[-1:]]) + return sigmas + + def sample_img2img(self, p, x, noise, conditioning, unconditional_conditioning, steps=None, image_conditioning=None): + steps, t_enc = setup_img2img_steps(p, steps) + + sigmas = self.get_sigmas(p, steps) + sigma_sched = sigmas[steps - t_enc - 1:] xi = x + noise * sigma_sched[0] @@ -488,18 +493,10 @@ class KDiffusionSampler: def sample(self, p, x, conditioning, unconditional_conditioning, steps=None, image_conditioning = None): steps = steps or p.steps - if p.sampler_noise_scheduler_override: - sigmas = p.sampler_noise_scheduler_override(steps) - elif self.config is not None and self.config.options.get('scheduler', None) == 'karras': - sigmas = k_diffusion.sampling.get_sigmas_karras(n=steps, sigma_min=0.1, sigma_max=10, device=shared.device) - else: - sigmas = self.model_wrap.get_sigmas(steps) + sigmas = self.get_sigmas(p, steps) x = x * sigmas[0] - if self.funcname in ['sample_dpm_2_ancestral', 'sample_dpm_2']: - sigmas = torch.cat([sigmas[:-2], sigmas[-1:]]) - extra_params_kwargs = self.initialize(p) if 'sigma_min' in inspect.signature(self.func).parameters: extra_params_kwargs['sigma_min'] = self.model_wrap.sigmas[0].item() From 1d9eaf94e32e2d6b817a6796ffa7b0a814580015 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 24 Dec 2022 09:27:41 +0300 Subject: [PATCH 44/59] add blendmodes to requirements_versions, remove aenum as it's already required by blendmodes --- requirements.txt | 1 - requirements_versions.txt | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 85e51575..5bed694e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ -aenum blendmodes accelerate basicsr diff --git a/requirements_versions.txt b/requirements_versions.txt index 7208aeba..c126c8c4 100644 --- a/requirements_versions.txt +++ b/requirements_versions.txt @@ -1,3 +1,4 @@ +blendmodes==2022 transformers==4.19.2 accelerate==0.12.0 basicsr==1.4.2 From 9441c28c947588d756e279a8cd5db6c0b4a8d2e4 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 24 Dec 2022 09:46:35 +0300 Subject: [PATCH 45/59] add an option for img2img background color --- modules/images.py | 11 +++++++++++ modules/processing.py | 2 +- modules/shared.py | 1 + modules/ui.py | 2 +- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/modules/images.py b/modules/images.py index ad97980c..8bcbc8d9 100644 --- a/modules/images.py +++ b/modules/images.py @@ -622,3 +622,14 @@ def image_data(data): pass return '', None + + +def flatten(img, bgcolor): + """replaces transparency with bgcolor (example: "#ffffff"), returning an RGB mode image with no transparency""" + + if img.mode == "RGBA": + background = Image.new('RGBA', img.size, bgcolor) + background.paste(img, mask=img) + img = background + + return img.convert('RGB') diff --git a/modules/processing.py b/modules/processing.py index bc841837..7c4bcd74 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -832,7 +832,7 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): self.color_corrections = [] imgs = [] for img in self.init_images: - image = img.convert("RGB") + image = images.flatten(img, opts.img2img_background_color) if crop_region is None: image = images.resize_image(self.resize_mode, image, self.width, self.height) diff --git a/modules/shared.py b/modules/shared.py index 215c1358..dcce9299 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -363,6 +363,7 @@ options_templates.update(options_section(('sd', "Stable Diffusion"), { "initial_noise_multiplier": OptionInfo(1.0, "Noise multiplier for img2img", gr.Slider, {"minimum": 0.5, "maximum": 1.5, "step": 0.01 }), "img2img_color_correction": OptionInfo(False, "Apply color correction to img2img results to match original colors."), "img2img_fix_steps": OptionInfo(False, "With img2img, do exactly the amount of steps the slider specifies (normally you'd do less with less denoising)."), + "img2img_background_color": OptionInfo("#ffffff", "With img2img, fill image's transparent parts with this color.", gr.ColorPicker, {}), "enable_quantization": OptionInfo(False, "Enable quantization in K samplers for sharper and cleaner results. This may change existing seeds. Requires restart to apply."), "enable_emphasis": OptionInfo(True, "Emphasis: use (text) to make model pay more attention to text and [text] to make it pay less attention"), "use_old_emphasis_implementation": OptionInfo(False, "Use old emphasis implementation. Can be useful to reproduce old seeds."), diff --git a/modules/ui.py b/modules/ui.py index 28481e33..76919b0f 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -812,7 +812,7 @@ def create_ui(): with gr.Tabs(elem_id="mode_img2img") as tabs_img2img_mode: with gr.TabItem('img2img', id='img2img'): - init_img = gr.Image(label="Image for img2img", elem_id="img2img_image", show_label=False, source="upload", interactive=True, type="pil", tool=cmd_opts.gradio_img2img_tool).style(height=480) + init_img = gr.Image(label="Image for img2img", elem_id="img2img_image", show_label=False, source="upload", interactive=True, type="pil", tool=cmd_opts.gradio_img2img_tool, image_mode="RGBA").style(height=480) with gr.TabItem('Inpaint', id='inpaint'): init_img_with_mask = gr.Image(label="Image for inpainting with mask", show_label=False, elem_id="img2maskimg", source="upload", interactive=True, type="pil", tool=cmd_opts.gradio_inpaint_tool, image_mode="RGBA").style(height=480) From c0a8401b5a8368d03bb14fc63abbdedb1e802d8d Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 24 Dec 2022 11:12:17 +0300 Subject: [PATCH 46/59] rename the option for img2img latent upscale --- modules/processing.py | 2 +- modules/ui.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/processing.py b/modules/processing.py index 75b0067c..d2288f26 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -846,7 +846,7 @@ class StableDiffusionProcessingImg2Img(StableDiffusionProcessing): self.overlay_images.append(image_masked.convert('RGBA')) - # crop_region is not none iif we are doing inpaint full res + # crop_region is not None if we are doing inpaint full res if crop_region is not None: image = image.crop(crop_region) image = images.resize_image(2, image, self.width, self.height) diff --git a/modules/ui.py b/modules/ui.py index faba69a4..9dec61d5 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -857,7 +857,7 @@ def create_ui(): img2img_batch_output_dir = gr.Textbox(label="Output directory", **shared.hide_dirs) with gr.Row(): - resize_mode = gr.Radio(label="Resize mode", elem_id="resize_mode", show_label=False, choices=["Just resize", "Crop and resize", "Resize and fill", "Upscale Latent Space"], type="index", value="Just resize") + resize_mode = gr.Radio(label="Resize mode", elem_id="resize_mode", show_label=False, choices=["Just resize", "Crop and resize", "Resize and fill", "Just resize (latent upscale)"], type="index", value="Just resize") steps = gr.Slider(minimum=1, maximum=150, step=1, label="Sampling Steps", value=20) sampler_index = gr.Radio(label='Sampling method', choices=[x.name for x in samplers_for_img2img], value=samplers_for_img2img[0].name, type="index") From 11dd79e346bd780bc5c3119df962e7a9c20f2493 Mon Sep 17 00:00:00 2001 From: AbstractQbit <38468635+AbstractQbit@users.noreply.github.com> Date: Sat, 24 Dec 2022 14:00:17 +0300 Subject: [PATCH 47/59] Add an option for faster low quality previews --- modules/sd_samplers.py | 23 ++++++++++++++++------- modules/shared.py | 5 +++-- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/modules/sd_samplers.py b/modules/sd_samplers.py index d26e48dc..fbb56af4 100644 --- a/modules/sd_samplers.py +++ b/modules/sd_samplers.py @@ -106,20 +106,29 @@ def setup_img2img_steps(p, steps=None): return steps, t_enc -def single_sample_to_image(sample): - x_sample = processing.decode_first_stage(shared.sd_model, sample.unsqueeze(0))[0] +def single_sample_to_image(sample, approximation=False): + if approximation: + # https://discuss.huggingface.co/t/decoding-latents-to-rgb-without-upscaling/23204/2 + coefs = torch.tensor( + [[ 0.298, 0.207, 0.208], + [ 0.187, 0.286, 0.173], + [-0.158, 0.189, 0.264], + [-0.184, -0.271, -0.473]]).to(sample.device) + x_sample = torch.einsum("lxy,lr -> rxy", sample, coefs) + else: + x_sample = processing.decode_first_stage(shared.sd_model, sample.unsqueeze(0))[0] x_sample = torch.clamp((x_sample + 1.0) / 2.0, min=0.0, max=1.0) x_sample = 255. * np.moveaxis(x_sample.cpu().numpy(), 0, 2) x_sample = x_sample.astype(np.uint8) return Image.fromarray(x_sample) -def sample_to_image(samples, index=0): - return single_sample_to_image(samples[index]) +def sample_to_image(samples, index=0, approximation=False): + return single_sample_to_image(samples[index], approximation) -def samples_to_image_grid(samples): - return images.image_grid([single_sample_to_image(sample) for sample in samples]) +def samples_to_image_grid(samples, approximation=False): + return images.image_grid([single_sample_to_image(sample, approximation) for sample in samples]) def store_latent(decoded): @@ -127,7 +136,7 @@ def store_latent(decoded): if opts.show_progress_every_n_steps > 0 and shared.state.sampling_step % opts.show_progress_every_n_steps == 0: if not shared.parallel_processing_allowed: - shared.state.current_image = sample_to_image(decoded) + shared.state.current_image = sample_to_image(decoded, approximation=opts.show_progress_approximate) class InterruptedException(BaseException): diff --git a/modules/shared.py b/modules/shared.py index 8ea3b441..1067b1d3 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -212,9 +212,9 @@ class State: import modules.sd_samplers if opts.show_progress_grid: - self.current_image = modules.sd_samplers.samples_to_image_grid(self.current_latent) + self.current_image = modules.sd_samplers.samples_to_image_grid(self.current_latent, approximation=opts.show_progress_approximate) else: - self.current_image = modules.sd_samplers.sample_to_image(self.current_latent) + self.current_image = modules.sd_samplers.sample_to_image(self.current_latent, approximation=opts.show_progress_approximate) self.current_image_sampling_step = self.sampling_step @@ -391,6 +391,7 @@ options_templates.update(options_section(('interrogate', "Interrogate Options"), options_templates.update(options_section(('ui', "User interface"), { "show_progressbar": OptionInfo(True, "Show progressbar"), "show_progress_every_n_steps": OptionInfo(0, "Show image creation progress every N sampling steps. Set to 0 to disable. Set to -1 to show after completion of batch.", gr.Slider, {"minimum": -1, "maximum": 32, "step": 1}), + "show_progress_approximate": OptionInfo(False, "Calculate small previews using fast linear approximation instead of VAE"), "show_progress_grid": OptionInfo(True, "Show previews of all images generated in a batch as a grid"), "return_grid": OptionInfo(True, "Show grid in results for web"), "do_not_show_images": OptionInfo(False, "Do not show any images in results for web"), From 6247f21a637399900643a4915e8a223688e0ed22 Mon Sep 17 00:00:00 2001 From: Philpax Date: Sat, 24 Dec 2022 22:04:53 +1100 Subject: [PATCH 48/59] fix(api): don't save extras output to disk --- modules/api/api.py | 6 +++--- modules/extras.py | 17 +++++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index 3257445d..b43dd16b 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -181,7 +181,7 @@ class Api: reqDict['image'] = decode_base64_to_image(reqDict['image']) with self.queue_lock: - result = run_extras(extras_mode=0, image_folder="", input_dir="", output_dir="", **reqDict) + result = run_extras(extras_mode=0, image_folder="", input_dir="", output_dir="", save_output=False, **reqDict) return ExtrasSingleImageResponse(image=encode_pil_to_base64(result[0][0]), html_info=result[1]) @@ -197,7 +197,7 @@ class Api: reqDict.pop('imageList') with self.queue_lock: - result = run_extras(extras_mode=1, image="", input_dir="", output_dir="", **reqDict) + result = run_extras(extras_mode=1, image="", input_dir="", output_dir="", save_output=False, **reqDict) return ExtrasBatchImagesResponse(images=list(map(encode_pil_to_base64, result[0])), html_info=result[1]) @@ -322,7 +322,7 @@ class Api: def get_artists(self): return [{"name":x[0], "score":x[1], "category":x[2]} for x in shared.artist_db.artists] - + def refresh_checkpoints(self): shared.refresh_checkpoints() diff --git a/modules/extras.py b/modules/extras.py index 6fa7d856..68939dea 100644 --- a/modules/extras.py +++ b/modules/extras.py @@ -55,7 +55,7 @@ class LruCache(OrderedDict): cached_images: LruCache = LruCache(max_size=5) -def run_extras(extras_mode, resize_mode, image, image_folder, input_dir, output_dir, show_extras_results, gfpgan_visibility, codeformer_visibility, codeformer_weight, upscaling_resize, upscaling_resize_w, upscaling_resize_h, upscaling_crop, extras_upscaler_1, extras_upscaler_2, extras_upscaler_2_visibility, upscale_first: bool): +def run_extras(extras_mode, resize_mode, image, image_folder, input_dir, output_dir, show_extras_results, gfpgan_visibility, codeformer_visibility, codeformer_weight, upscaling_resize, upscaling_resize_w, upscaling_resize_h, upscaling_crop, extras_upscaler_1, extras_upscaler_2, extras_upscaler_2_visibility, upscale_first: bool, save_output: bool = True): devices.torch_gc() imageArr = [] @@ -193,14 +193,15 @@ def run_extras(extras_mode, resize_mode, image, image_folder, input_dir, output_ else: basename = '' - # Add upscaler name as a suffix. - suffix = f"-{shared.sd_upscalers[extras_upscaler_1].name}" if shared.opts.use_upscaler_name_as_suffix else "" - # Add second upscaler if applicable. - if suffix and extras_upscaler_2 and extras_upscaler_2_visibility: - suffix += f"-{shared.sd_upscalers[extras_upscaler_2].name}" + if save_output: + # Add upscaler name as a suffix. + suffix = f"-{shared.sd_upscalers[extras_upscaler_1].name}" if shared.opts.use_upscaler_name_as_suffix else "" + # Add second upscaler if applicable. + if suffix and extras_upscaler_2 and extras_upscaler_2_visibility: + suffix += f"-{shared.sd_upscalers[extras_upscaler_2].name}" - images.save_image(image, path=outpath, basename=basename, seed=None, prompt=None, extension=opts.samples_format, info=info, short_filename=True, - no_prompt=True, grid=False, pnginfo_section_name="extras", existing_info=existing_pnginfo, forced_filename=None, suffix=suffix) + images.save_image(image, path=outpath, basename=basename, seed=None, prompt=None, extension=opts.samples_format, info=info, short_filename=True, + no_prompt=True, grid=False, pnginfo_section_name="extras", existing_info=existing_pnginfo, forced_filename=None, suffix=suffix) if opts.enable_pnginfo: image.info = existing_pnginfo From 5a650055de3792223a91925aba8130ebdee29e35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?linuxmobile=20=28=20=E3=83=AA=E3=83=8A=E3=83=83=E3=82=AF?= =?UTF-8?q?=E3=82=B9=20=29?= Date: Sat, 24 Dec 2022 09:25:35 -0300 Subject: [PATCH 49/59] Removed lenght in sd_model at line 115 Commit eba60a4 is what is causing this error, delete the length check in sd_model starting at line 115 and it's fine. https://github.com/AUTOMATIC1111/stable-diffusion-webui/issues/5971#issuecomment-1364507379 --- modules/sd_models.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/modules/sd_models.py b/modules/sd_models.py index 1254e5ae..6ca06211 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -111,9 +111,6 @@ def model_hash(filename): def select_checkpoint(): model_checkpoint = shared.opts.sd_model_checkpoint - - if len(model_checkpoint) == 0: - model_checkpoint = shared.default_sd_model_file checkpoint_info = checkpoints_list.get(model_checkpoint, None) if checkpoint_info is not None: From 03d7b394539558f6f560155d87a4fc66eb675e30 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 24 Dec 2022 12:40:32 +0300 Subject: [PATCH 50/59] added an option to filter out deepbooru tags --- modules/deepbooru.py | 4 +++- modules/shared.py | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/deepbooru.py b/modules/deepbooru.py index dfc83357..122fce7f 100644 --- a/modules/deepbooru.py +++ b/modules/deepbooru.py @@ -79,7 +79,9 @@ class DeepDanbooru: res = [] - for tag in tags: + filtertags = set([x.strip().replace(' ', '_') for x in shared.opts.deepbooru_filter_tags.split(",")]) + + for tag in [x for x in tags if x not in filtertags]: probability = probability_dict[tag] tag_outformat = tag if use_spaces: diff --git a/modules/shared.py b/modules/shared.py index 8ea3b441..a75de535 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -386,6 +386,7 @@ options_templates.update(options_section(('interrogate', "Interrogate Options"), "deepbooru_sort_alpha": OptionInfo(True, "Interrogate: deepbooru sort alphabetically"), "deepbooru_use_spaces": OptionInfo(False, "use spaces for tags in deepbooru"), "deepbooru_escape": OptionInfo(True, "escape (\\) brackets in deepbooru (so they are used as literal brackets and not for emphasis)"), + "deepbooru_filter_tags": OptionInfo("", "filter out those tags from deepbooru output (separated by comma)"), })) options_templates.update(options_section(('ui', "User interface"), { From 0b8acce6a9a1418fa88a506450cd1b92e2d48986 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 24 Dec 2022 18:38:16 +0300 Subject: [PATCH 51/59] separate part of denoiser code into a function to make it easier for extensions to override it --- modules/sd_samplers.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/modules/sd_samplers.py b/modules/sd_samplers.py index d26e48dc..8efe74df 100644 --- a/modules/sd_samplers.py +++ b/modules/sd_samplers.py @@ -288,6 +288,16 @@ class CFGDenoiser(torch.nn.Module): self.init_latent = None self.step = 0 + def combine_denoised(self, x_out, conds_list, uncond, cond_scale): + denoised_uncond = x_out[-uncond.shape[0]:] + denoised = torch.clone(denoised_uncond) + + for i, conds in enumerate(conds_list): + for cond_index, weight in conds: + denoised[i] += (x_out[cond_index] - denoised_uncond[i]) * (weight * cond_scale) + + return denoised + def forward(self, x, sigma, uncond, cond, cond_scale, image_cond): if state.interrupted or state.skipped: raise InterruptedException @@ -329,12 +339,7 @@ class CFGDenoiser(torch.nn.Module): x_out[-uncond.shape[0]:] = self.inner_model(x_in[-uncond.shape[0]:], sigma_in[-uncond.shape[0]:], cond={"c_crossattn": [uncond], "c_concat": [image_cond_in[-uncond.shape[0]:]]}) - denoised_uncond = x_out[-uncond.shape[0]:] - denoised = torch.clone(denoised_uncond) - - for i, conds in enumerate(conds_list): - for cond_index, weight in conds: - denoised[i] += (x_out[cond_index] - denoised_uncond[i]) * (weight * cond_scale) + denoised = self.combine_denoised(x_out, conds_list, uncond, cond_scale) if self.mask is not None: denoised = self.init_latent * self.mask + self.nmask * denoised From 3bf5591efe9a9f219c6088be322a87adc4f48f95 Mon Sep 17 00:00:00 2001 From: Yuval Aboulafia Date: Sat, 24 Dec 2022 21:35:29 +0200 Subject: [PATCH 52/59] fix F541 f-string without any placeholders --- extensions-builtin/LDSR/ldsr_model_arch.py | 2 +- modules/codeformer/vqgan_arch.py | 4 ++-- modules/hypernetworks/hypernetwork.py | 4 ++-- modules/images.py | 2 +- modules/interrogate.py | 2 +- modules/safe.py | 8 ++++---- modules/sd_models.py | 8 ++++---- modules/sd_vae.py | 2 +- modules/textual_inversion/textual_inversion.py | 2 +- scripts/prompts_from_file.py | 2 +- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/extensions-builtin/LDSR/ldsr_model_arch.py b/extensions-builtin/LDSR/ldsr_model_arch.py index f5bd8ae4..0ad49f4e 100644 --- a/extensions-builtin/LDSR/ldsr_model_arch.py +++ b/extensions-builtin/LDSR/ldsr_model_arch.py @@ -26,7 +26,7 @@ class LDSR: global cached_ldsr_model if shared.opts.ldsr_cached and cached_ldsr_model is not None: - print(f"Loading model from cache") + print("Loading model from cache") model: torch.nn.Module = cached_ldsr_model else: print(f"Loading model from {self.modelPath}") diff --git a/modules/codeformer/vqgan_arch.py b/modules/codeformer/vqgan_arch.py index c06c590c..e7293683 100644 --- a/modules/codeformer/vqgan_arch.py +++ b/modules/codeformer/vqgan_arch.py @@ -382,7 +382,7 @@ class VQAutoEncoder(nn.Module): self.load_state_dict(torch.load(model_path, map_location='cpu')['params']) logger.info(f'vqgan is loaded from: {model_path} [params]') else: - raise ValueError(f'Wrong params!') + raise ValueError('Wrong params!') def forward(self, x): @@ -431,7 +431,7 @@ class VQGANDiscriminator(nn.Module): elif 'params' in chkpt: self.load_state_dict(torch.load(model_path, map_location='cpu')['params']) else: - raise ValueError(f'Wrong params!') + raise ValueError('Wrong params!') def forward(self, x): return self.main(x) \ No newline at end of file diff --git a/modules/hypernetworks/hypernetwork.py b/modules/hypernetworks/hypernetwork.py index c406ffb3..9d3034ae 100644 --- a/modules/hypernetworks/hypernetwork.py +++ b/modules/hypernetworks/hypernetwork.py @@ -277,7 +277,7 @@ def load_hypernetwork(filename): print(traceback.format_exc(), file=sys.stderr) else: if shared.loaded_hypernetwork is not None: - print(f"Unloading hypernetwork") + print("Unloading hypernetwork") shared.loaded_hypernetwork = None @@ -417,7 +417,7 @@ def train_hypernetwork(hypernetwork_name, learn_rate, batch_size, gradient_step, initial_step = hypernetwork.step or 0 if initial_step >= steps: - shared.state.textinfo = f"Model has already been trained beyond specified max steps" + shared.state.textinfo = "Model has already been trained beyond specified max steps" return hypernetwork, filename scheduler = LearnRateScheduler(learn_rate, steps, initial_step) diff --git a/modules/images.py b/modules/images.py index 809ad9f7..31d4528d 100644 --- a/modules/images.py +++ b/modules/images.py @@ -599,7 +599,7 @@ def read_info_from_image(image): Negative prompt: {json_info["uc"]} Steps: {json_info["steps"]}, Sampler: {sampler}, CFG scale: {json_info["scale"]}, Seed: {json_info["seed"]}, Size: {image.width}x{image.height}, Clip skip: 2, ENSD: 31337""" except Exception: - print(f"Error parsing NovelAI image generation parameters:", file=sys.stderr) + print("Error parsing NovelAI image generation parameters:", file=sys.stderr) print(traceback.format_exc(), file=sys.stderr) return geninfo, items diff --git a/modules/interrogate.py b/modules/interrogate.py index 0068b81c..46935210 100644 --- a/modules/interrogate.py +++ b/modules/interrogate.py @@ -172,7 +172,7 @@ class InterrogateModels: res += ", " + match except Exception: - print(f"Error interrogating", file=sys.stderr) + print("Error interrogating", file=sys.stderr) print(traceback.format_exc(), file=sys.stderr) res += "" diff --git a/modules/safe.py b/modules/safe.py index 479c8b86..1d4c20b9 100644 --- a/modules/safe.py +++ b/modules/safe.py @@ -137,15 +137,15 @@ def load_with_extra(filename, extra_handler=None, *args, **kwargs): except pickle.UnpicklingError: print(f"Error verifying pickled file from {filename}:", file=sys.stderr) print(traceback.format_exc(), file=sys.stderr) - print(f"-----> !!!! The file is most likely corrupted !!!! <-----", file=sys.stderr) - print(f"You can skip this check with --disable-safe-unpickle commandline argument, but that is not going to help you.\n\n", file=sys.stderr) + print("-----> !!!! The file is most likely corrupted !!!! <-----", file=sys.stderr) + print("You can skip this check with --disable-safe-unpickle commandline argument, but that is not going to help you.\n\n", file=sys.stderr) return None except Exception: print(f"Error verifying pickled file from {filename}:", file=sys.stderr) print(traceback.format_exc(), file=sys.stderr) - print(f"\nThe file may be malicious, so the program is not going to read it.", file=sys.stderr) - print(f"You can skip this check with --disable-safe-unpickle commandline argument.\n\n", file=sys.stderr) + print("\nThe file may be malicious, so the program is not going to read it.", file=sys.stderr) + print("You can skip this check with --disable-safe-unpickle commandline argument.\n\n", file=sys.stderr) return None return unsafe_torch_load(filename, *args, **kwargs) diff --git a/modules/sd_models.py b/modules/sd_models.py index 6ca06211..ecdd91c5 100644 --- a/modules/sd_models.py +++ b/modules/sd_models.py @@ -117,13 +117,13 @@ def select_checkpoint(): return checkpoint_info if len(checkpoints_list) == 0: - print(f"No checkpoints found. When searching for checkpoints, looked at:", file=sys.stderr) + print("No checkpoints found. When searching for checkpoints, looked at:", file=sys.stderr) if shared.cmd_opts.ckpt is not None: print(f" - file {os.path.abspath(shared.cmd_opts.ckpt)}", file=sys.stderr) print(f" - directory {model_path}", file=sys.stderr) if shared.cmd_opts.ckpt_dir is not None: print(f" - directory {os.path.abspath(shared.cmd_opts.ckpt_dir)}", file=sys.stderr) - print(f"Can't run without a checkpoint. Find and place a .ckpt file into any of those locations. The program will exit.", file=sys.stderr) + print("Can't run without a checkpoint. Find and place a .ckpt file into any of those locations. The program will exit.", file=sys.stderr) exit(1) checkpoint_info = next(iter(checkpoints_list.values())) @@ -324,7 +324,7 @@ def load_model(checkpoint_info=None): script_callbacks.model_loaded_callback(sd_model) - print(f"Model loaded.") + print("Model loaded.") return sd_model @@ -359,5 +359,5 @@ def reload_model_weights(sd_model=None, info=None): if not shared.cmd_opts.lowvram and not shared.cmd_opts.medvram: sd_model.to(devices.device) - print(f"Weights loaded.") + print("Weights loaded.") return sd_model diff --git a/modules/sd_vae.py b/modules/sd_vae.py index 25638a83..3856418e 100644 --- a/modules/sd_vae.py +++ b/modules/sd_vae.py @@ -208,5 +208,5 @@ def reload_vae_weights(sd_model=None, vae_file="auto"): if not shared.cmd_opts.lowvram and not shared.cmd_opts.medvram: sd_model.to(devices.device) - print(f"VAE Weights loaded.") + print("VAE Weights loaded.") return sd_model diff --git a/modules/textual_inversion/textual_inversion.py b/modules/textual_inversion/textual_inversion.py index daf3997b..f6112578 100644 --- a/modules/textual_inversion/textual_inversion.py +++ b/modules/textual_inversion/textual_inversion.py @@ -263,7 +263,7 @@ def train_embedding(embedding_name, learn_rate, batch_size, gradient_step, data_ initial_step = embedding.step or 0 if initial_step >= steps: - shared.state.textinfo = f"Model has already been trained beyond specified max steps" + shared.state.textinfo = "Model has already been trained beyond specified max steps" return embedding, filename scheduler = LearnRateScheduler(learn_rate, steps, initial_step) diff --git a/scripts/prompts_from_file.py b/scripts/prompts_from_file.py index 6e118ddb..e8386ed2 100644 --- a/scripts/prompts_from_file.py +++ b/scripts/prompts_from_file.py @@ -140,7 +140,7 @@ class Script(scripts.Script): try: args = cmdargs(line) except Exception: - print(f"Error parsing line [line] as commandline:", file=sys.stderr) + print(f"Error parsing line {line} as commandline:", file=sys.stderr) print(traceback.format_exc(), file=sys.stderr) args = {"prompt": line} else: From 56e557c6ff8a6480887c9c585bf908045ee8e791 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 24 Dec 2022 22:39:00 +0300 Subject: [PATCH 53/59] added cheap NN approximation for VAE --- javascript/hints.js | 5 +++- models/VAE-approx/model.pt | Bin 0 -> 213777 bytes modules/sd_samplers.py | 29 ++++++++++--------- modules/sd_vae_approx.py | 58 +++++++++++++++++++++++++++++++++++++ modules/shared.py | 6 ++-- 5 files changed, 81 insertions(+), 17 deletions(-) create mode 100644 models/VAE-approx/model.pt create mode 100644 modules/sd_vae_approx.py diff --git a/javascript/hints.js b/javascript/hints.js index a739a177..63e17e05 100644 --- a/javascript/hints.js +++ b/javascript/hints.js @@ -97,7 +97,10 @@ titles = { "Learning rate": "how fast should the training go. Low values will take longer to train, high values may fail to converge (not generate accurate results) and/or may break the embedding (This has happened if you see Loss: nan in the training info textbox. If this happens, you need to manually restore your embedding from an older not-broken backup).\n\nYou can set a single numeric value, or multiple learning rates using the syntax:\n\n rate_1:max_steps_1, rate_2:max_steps_2, ...\n\nEG: 0.005:100, 1e-3:1000, 1e-5\n\nWill train with rate of 0.005 for first 100 steps, then 1e-3 until 1000 steps, then 1e-5 for all remaining steps.", - "Clip skip": "Early stopping parameter for CLIP model; 1 is stop at last layer as usual, 2 is stop at penultimate layer, etc." + "Clip skip": "Early stopping parameter for CLIP model; 1 is stop at last layer as usual, 2 is stop at penultimate layer, etc.", + + "Approx NN": "Cheap neural network approximation. Very fast compared to VAE, but produces pictures with 4 times smaller horizontal/vertical resoluton and lower quality.", + "Approx cheap": "Very cheap approximation. Very fast compared to VAE, but produces pictures with 8 times smaller horizontal/vertical resoluton and extremely low quality." } diff --git a/models/VAE-approx/model.pt b/models/VAE-approx/model.pt new file mode 100644 index 0000000000000000000000000000000000000000..8bda9d6e952041db7733eff61238a26dd1ea6cda GIT binary patch literal 213777 zcmaf)30O^C7^s^xr-=qa(Ik{o8ut2*lp!S)l0rf%m7E4mqSB~QsVFoU8iWR+v)6Y@ z88Ry&Lu5!~NXn25H^2M;_y6yG?!D)E&hzY6=e+wp>#Y5)v%YWbC3eCh0s`XV0{_Qj zq`)WvKmQFLz6KlIgWUDE`1sDVk`r+G-yTsy>(~4H`g*Jn^7h{xD7iRbgGYeJhB@Br zgCw~ElO4H&y3W$W==J`aw;Jk)czAny1#yMe2l)rA_tJL@4)XR5lynR5SQqT=yTL8U zV{@Q?fZJ9hNv^Q2uCv(i`vQkw>F(*l75VcP$$7s1?megSe7OUcb$xg1FL-Tp4G{ zfArRQy9aVddJUi3OT_CB+mI{!Z?>GBco0{97;PsW$5pTsjN>Z)fsG2{j&|fK{qtFk z{{F1W|G^n?RsM}rwU3ex;*R-Cbr@|o!fr$mcdV{mvz_Yj@vy4eANsf;?s!M;gnv|x z^#4*-{||cxSL5I8iFR^9+)2Mx<>I)Szg4yVz_f$7I*#1`^|j3STXph(aK>D{f8(au z3n&C}r~aj?ZYO6aY$qJVou+G-VJ9(sJgln!hi(wWo$kmr{6}@hA629OuuZr#{>?VF zQxD>r{8m+uydA|Lj)FKduhC_Lf3_yE^{CFAd@@bL1}n$JOMItJ8nr z=G+zkhCBc6*OmXXUtNB?y8eN!3gWJI`o$ZvWtfhOY|4>D;yd=DGhh2G;#0 zEBANDuK#z(ZurCZ2;y#ZfAiw(+Jm_9f4K?| z2ZFz@t^{2>o4-dv;vafa5Oxp{Vi zfzBg_|Mgc`=jKOp_j~=P&4_y-l6&yKZ8Nw9k=(-nwi$DaBDux?Z8PB>isT;tZ`(}n zkx1^*|F)TOOCq_Y|7|nlmPK;Q|7r^kT&KUpPE33#YLDuH;pyS`bIJd8vXd5={IAK* zVEQ~Ot^Z@H``>21KYx}g-)Axg3fZ52!Z;%89d)gfV+KC^LDV&QJg2*!Ui#($_e3UO zR;>lQNJIwu;!274+=Jx#qJzZd<5^#+w2f|AQ-R=&l4hX}ktK zzGfsSYKef2{|&lhu#&AEAwf!OWZC_hlfYwU9@Uz4nW5guh~8ezE;_W2g7hh|bn_2|#edJ-sK(>WY&`nZyZ5-!|b~Na}wIN#X&0*4qceE*WJgcrwf8L&qyh$opYsM07{f&4)b~0_~=qGdIrelxa zQRK;`z|?tF@ap6ol$Y5~0*|afg9JG+TWN?+(>tJ|*$N_TAClb0D4h8-o!xD^pFXnC zL7^Qt$-yUeP#fG%R_(h--d{^2Hw3gu|JqGBGee!OUB@T$XWNjh@+x}v;|!e9rwRT& z)};1xI-anJBx2JVsDH&-Cd=|Q*`nS{P1GlWciS#fyRe#sgxq51G%v+RIb9ZC^><*c z>}=Tlq!op)=wSYZ`=oj6DNGT6%M8A&Cr(9i;QI6^UXV7VOV$f=M%}Q)`KlS%IdLkv zAXda~=@X*963-F5ML8QQM{^d_R`S8Xj((@_sICG_4=(sj=06F6okhyP9BQT)GL-3B zdu<2}?czN!D&d_kIfJ9#in5{$EJ(C!D-|z)NKC{f>884|n0>+yWO9SQN*7>S!ⅈW$$Kt*J==X-zm@# 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

-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

#(&@O#Q+PJXjJin=PpXmOMvSF4t8 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 z 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 Ck+^>V literal 0 HcmV?d00001 diff --git a/modules/sd_samplers.py b/modules/sd_samplers.py index 27ef4ff8..177b5338 100644 --- a/modules/sd_samplers.py +++ b/modules/sd_samplers.py @@ -9,7 +9,7 @@ import k_diffusion.sampling import torchsde._brownian.brownian_interval import ldm.models.diffusion.ddim import ldm.models.diffusion.plms -from modules import prompt_parser, devices, processing, images +from modules import prompt_parser, devices, processing, images, sd_vae_approx from modules.shared import opts, cmd_opts, state import modules.shared as shared @@ -106,28 +106,31 @@ def setup_img2img_steps(p, steps=None): return steps, t_enc -def single_sample_to_image(sample, approximation=False): - if approximation: - # https://discuss.huggingface.co/t/decoding-latents-to-rgb-without-upscaling/23204/2 - coefs = torch.tensor( - [[ 0.298, 0.207, 0.208], - [ 0.187, 0.286, 0.173], - [-0.158, 0.189, 0.264], - [-0.184, -0.271, -0.473]]).to(sample.device) - x_sample = torch.einsum("lxy,lr -> rxy", sample, coefs) +approximation_indexes = {"Full": 0, "Approx NN": 1, "Approx cheap": 2} + + +def single_sample_to_image(sample, approximation=None): + if approximation is None: + approximation = approximation_indexes.get(opts.show_progress_type, 0) + + if approximation == 2: + x_sample = sd_vae_approx.cheap_approximation(sample) + elif approximation == 1: + x_sample = sd_vae_approx.model()(sample.to(devices.device, devices.dtype).unsqueeze(0))[0].detach() else: x_sample = processing.decode_first_stage(shared.sd_model, sample.unsqueeze(0))[0] + x_sample = torch.clamp((x_sample + 1.0) / 2.0, min=0.0, max=1.0) x_sample = 255. * np.moveaxis(x_sample.cpu().numpy(), 0, 2) x_sample = x_sample.astype(np.uint8) return Image.fromarray(x_sample) -def sample_to_image(samples, index=0, approximation=False): +def sample_to_image(samples, index=0, approximation=None): return single_sample_to_image(samples[index], approximation) -def samples_to_image_grid(samples, approximation=False): +def samples_to_image_grid(samples, approximation=None): return images.image_grid([single_sample_to_image(sample, approximation) for sample in samples]) @@ -136,7 +139,7 @@ def store_latent(decoded): if opts.show_progress_every_n_steps > 0 and shared.state.sampling_step % opts.show_progress_every_n_steps == 0: if not shared.parallel_processing_allowed: - shared.state.current_image = sample_to_image(decoded, approximation=opts.show_progress_approximate) + shared.state.current_image = sample_to_image(decoded) class InterruptedException(BaseException): diff --git a/modules/sd_vae_approx.py b/modules/sd_vae_approx.py new file mode 100644 index 00000000..0a58542d --- /dev/null +++ b/modules/sd_vae_approx.py @@ -0,0 +1,58 @@ +import os + +import torch +from torch import nn +from modules import devices, paths + +sd_vae_approx_model = None + + +class VAEApprox(nn.Module): + def __init__(self): + super(VAEApprox, self).__init__() + self.conv1 = nn.Conv2d(4, 8, (7, 7)) + self.conv2 = nn.Conv2d(8, 16, (5, 5)) + self.conv3 = nn.Conv2d(16, 32, (3, 3)) + self.conv4 = nn.Conv2d(32, 64, (3, 3)) + self.conv5 = nn.Conv2d(64, 32, (3, 3)) + self.conv6 = nn.Conv2d(32, 16, (3, 3)) + self.conv7 = nn.Conv2d(16, 8, (3, 3)) + self.conv8 = nn.Conv2d(8, 3, (3, 3)) + + def forward(self, x): + extra = 11 + x = nn.functional.interpolate(x, (x.shape[2] * 2, x.shape[3] * 2)) + x = nn.functional.pad(x, (extra, extra, extra, extra)) + + for layer in [self.conv1, self.conv2, self.conv3, self.conv4, self.conv5, self.conv6, self.conv7, self.conv8, ]: + x = layer(x) + x = nn.functional.leaky_relu(x, 0.1) + + return x + + +def model(): + global sd_vae_approx_model + + if sd_vae_approx_model is None: + sd_vae_approx_model = VAEApprox() + sd_vae_approx_model.load_state_dict(torch.load(os.path.join(paths.models_path, "VAE-approx", "model.pt"))) + sd_vae_approx_model.eval() + sd_vae_approx_model.to(devices.device, devices.dtype) + + return sd_vae_approx_model + + +def cheap_approximation(sample): + # https://discuss.huggingface.co/t/decoding-latents-to-rgb-without-upscaling/23204/2 + + coefs = torch.tensor([ + [0.298, 0.207, 0.208], + [0.187, 0.286, 0.173], + [-0.158, 0.189, 0.264], + [-0.184, -0.271, -0.473], + ]).to(sample.device) + + x_sample = torch.einsum("lxy,lr -> rxy", sample, coefs) + + return x_sample diff --git a/modules/shared.py b/modules/shared.py index eb3e5aec..3cc3c724 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -212,9 +212,9 @@ class State: import modules.sd_samplers if opts.show_progress_grid: - self.current_image = modules.sd_samplers.samples_to_image_grid(self.current_latent, approximation=opts.show_progress_approximate) + self.current_image = modules.sd_samplers.samples_to_image_grid(self.current_latent) else: - self.current_image = modules.sd_samplers.sample_to_image(self.current_latent, approximation=opts.show_progress_approximate) + self.current_image = modules.sd_samplers.sample_to_image(self.current_latent) self.current_image_sampling_step = self.sampling_step @@ -392,7 +392,7 @@ options_templates.update(options_section(('interrogate', "Interrogate Options"), options_templates.update(options_section(('ui', "User interface"), { "show_progressbar": OptionInfo(True, "Show progressbar"), "show_progress_every_n_steps": OptionInfo(0, "Show image creation progress every N sampling steps. Set to 0 to disable. Set to -1 to show after completion of batch.", gr.Slider, {"minimum": -1, "maximum": 32, "step": 1}), - "show_progress_approximate": OptionInfo(False, "Calculate small previews using fast linear approximation instead of VAE"), + "show_progress_type": OptionInfo("Full", "Image creation progress mode", gr.Radio, {"choices": ["Full", "Approx NN", "Approx cheap"]}), "show_progress_grid": OptionInfo(True, "Show previews of all images generated in a batch as a grid"), "return_grid": OptionInfo(True, "Show grid in results for web"), "do_not_show_images": OptionInfo(False, "Do not show any images in results for web"), From c5bdba2089dc7060be2631bcbc83313b6358cbf2 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sat, 24 Dec 2022 22:41:35 +0300 Subject: [PATCH 54/59] change wording a bit --- modules/shared.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/shared.py b/modules/shared.py index 3cc3c724..d4ddeea0 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -392,7 +392,7 @@ options_templates.update(options_section(('interrogate', "Interrogate Options"), options_templates.update(options_section(('ui', "User interface"), { "show_progressbar": OptionInfo(True, "Show progressbar"), "show_progress_every_n_steps": OptionInfo(0, "Show image creation progress every N sampling steps. Set to 0 to disable. Set to -1 to show after completion of batch.", gr.Slider, {"minimum": -1, "maximum": 32, "step": 1}), - "show_progress_type": OptionInfo("Full", "Image creation progress mode", gr.Radio, {"choices": ["Full", "Approx NN", "Approx cheap"]}), + "show_progress_type": OptionInfo("Full", "Image creation progress preview mode", gr.Radio, {"choices": ["Full", "Approx NN", "Approx cheap"]}), "show_progress_grid": OptionInfo(True, "Show previews of all images generated in a batch as a grid"), "return_grid": OptionInfo(True, "Show grid in results for web"), "do_not_show_images": OptionInfo(False, "Do not show any images in results for web"), From 5f1dfbbc959855fd90ba80c0c76301d2063772fa Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Sat, 24 Dec 2022 18:02:22 -0500 Subject: [PATCH 55/59] implement train api --- modules/api/api.py | 94 ++++++++++++++++++++++++++- modules/api/models.py | 9 +++ modules/hypernetworks/hypernetwork.py | 26 ++++++++ modules/hypernetworks/ui.py | 31 ++------- 4 files changed, 132 insertions(+), 28 deletions(-) diff --git a/modules/api/api.py b/modules/api/api.py index b43dd16b..1ceba75d 100644 --- a/modules/api/api.py +++ b/modules/api/api.py @@ -10,13 +10,17 @@ from fastapi.security import HTTPBasic, HTTPBasicCredentials from secrets import compare_digest import modules.shared as shared -from modules import sd_samplers, deepbooru +from modules import sd_samplers, deepbooru, sd_hijack from modules.api.models import * from modules.processing import StableDiffusionProcessingTxt2Img, StableDiffusionProcessingImg2Img, process_images from modules.extras import run_extras, run_pnginfo +from modules.textual_inversion.textual_inversion import create_embedding, train_embedding +from modules.textual_inversion.preprocess import preprocess +from modules.hypernetworks.hypernetwork import create_hypernetwork, train_hypernetwork from PIL import PngImagePlugin,Image from modules.sd_models import checkpoints_list from modules.realesrgan_model import get_realesrgan_models +from modules import devices from typing import List def upscaler_to_index(name: str): @@ -97,6 +101,11 @@ class Api: self.add_api_route("/sdapi/v1/artist-categories", self.get_artists_categories, methods=["GET"], response_model=List[str]) self.add_api_route("/sdapi/v1/artists", self.get_artists, methods=["GET"], response_model=List[ArtistItem]) self.add_api_route("/sdapi/v1/refresh-checkpoints", self.refresh_checkpoints, methods=["POST"]) + self.add_api_route("/sdapi/v1/create/embedding", self.create_embedding, methods=["POST"], response_model=CreateResponse) + self.add_api_route("/sdapi/v1/create/hypernetwork", self.create_hypernetwork, methods=["POST"], response_model=CreateResponse) + self.add_api_route("/sdapi/v1/preprocess", self.preprocess, methods=["POST"], response_model=PreprocessResponse) + self.add_api_route("/sdapi/v1/train/embedding", self.train_embedding, methods=["POST"], response_model=TrainResponse) + self.add_api_route("/sdapi/v1/train/hypernetwork", self.train_hypernetwork, methods=["POST"], response_model=TrainResponse) def add_api_route(self, path: str, endpoint, **kwargs): if shared.cmd_opts.api_auth: @@ -326,6 +335,89 @@ class Api: def refresh_checkpoints(self): shared.refresh_checkpoints() + def create_embedding(self, args: dict): + try: + shared.state.begin() + filename = create_embedding(**args) # create empty embedding + sd_hijack.model_hijack.embedding_db.load_textual_inversion_embeddings() # reload embeddings so new one can be immediately used + shared.state.end() + return CreateResponse(info = "create embedding filename: {filename}".format(filename = filename)) + except AssertionError as e: + shared.state.end() + return TrainResponse(info = "create embedding error: {error}".format(error = e)) + + def create_hypernetwork(self, args: dict): + try: + shared.state.begin() + filename = create_hypernetwork(**args) # create empty embedding + shared.state.end() + return CreateResponse(info = "create hypernetwork filename: {filename}".format(filename = filename)) + except AssertionError as e: + shared.state.end() + return TrainResponse(info = "create hypernetwork error: {error}".format(error = e)) + + def preprocess(self, args: dict): + try: + shared.state.begin() + preprocess(**args) # quick operation unless blip/booru interrogation is enabled + shared.state.end() + return PreprocessResponse(info = 'preprocess complete') + except KeyError as e: + shared.state.end() + return PreprocessResponse(info = "preprocess error: invalid token: {error}".format(error = e)) + except AssertionError as e: + shared.state.end() + return PreprocessResponse(info = "preprocess error: {error}".format(error = e)) + except FileNotFoundError as e: + shared.state.end() + return PreprocessResponse(info = 'preprocess error: {error}'.format(error = e)) + + def train_embedding(self, args: dict): + try: + shared.state.begin() + apply_optimizations = shared.opts.training_xattention_optimizations + error = None + filename = '' + if not apply_optimizations: + sd_hijack.undo_optimizations() + try: + embedding, filename = train_embedding(**args) # can take a long time to complete + except Exception as e: + error = e + finally: + if not apply_optimizations: + sd_hijack.apply_optimizations() + shared.state.end() + return TrainResponse(info = "train embedding complete: filename: {filename} error: {error}".format(filename = filename, error = error)) + except AssertionError as msg: + shared.state.end() + return TrainResponse(info = "train embedding error: {msg}".format(msg = msg)) + + def train_hypernetwork(self, args: dict): + try: + shared.state.begin() + initial_hypernetwork = shared.loaded_hypernetwork + apply_optimizations = shared.opts.training_xattention_optimizations + error = None + filename = '' + if not apply_optimizations: + sd_hijack.undo_optimizations() + try: + hypernetwork, filename = train_hypernetwork(*args) + except Exception as e: + error = e + finally: + shared.loaded_hypernetwork = initial_hypernetwork + shared.sd_model.cond_stage_model.to(devices.device) + shared.sd_model.first_stage_model.to(devices.device) + if not apply_optimizations: + sd_hijack.apply_optimizations() + shared.state.end() + return TrainResponse(info = "train embedding complete: filename: {filename} error: {error}".format(filename = filename, error = error)) + except AssertionError as msg: + shared.state.end() + return TrainResponse(info = "train embedding error: {error}".format(error = error)) + def launch(self, server_name, port): self.app.include_router(self.router) uvicorn.run(self.app, host=server_name, port=port) diff --git a/modules/api/models.py b/modules/api/models.py index a22bc6b3..c446ce7a 100644 --- a/modules/api/models.py +++ b/modules/api/models.py @@ -175,6 +175,15 @@ class InterrogateRequest(BaseModel): class InterrogateResponse(BaseModel): caption: str = Field(default=None, title="Caption", description="The generated caption for the image.") +class TrainResponse(BaseModel): + info: str = Field(title="Train info", description="Response string from train embedding or hypernetwork task.") + +class CreateResponse(BaseModel): + info: str = Field(title="Create info", description="Response string from create embedding or hypernetwork task.") + +class PreprocessResponse(BaseModel): + info: str = Field(title="Preprocess info", description="Response string from preprocessing task.") + fields = {} for key, metadata in opts.data_labels.items(): value = opts.data.get(key) diff --git a/modules/hypernetworks/hypernetwork.py b/modules/hypernetworks/hypernetwork.py index c406ffb3..3182ff03 100644 --- a/modules/hypernetworks/hypernetwork.py +++ b/modules/hypernetworks/hypernetwork.py @@ -378,6 +378,32 @@ def report_statistics(loss_info:dict): print(e) +def create_hypernetwork(name, enable_sizes, overwrite_old, layer_structure=None, activation_func=None, weight_init=None, add_layer_norm=False, use_dropout=False): + # Remove illegal characters from name. + name = "".join( x for x in name if (x.isalnum() or x in "._- ")) + + fn = os.path.join(shared.cmd_opts.hypernetwork_dir, f"{name}.pt") + if not overwrite_old: + assert not os.path.exists(fn), f"file {fn} already exists" + + if type(layer_structure) == str: + layer_structure = [float(x.strip()) for x in layer_structure.split(",")] + + hypernet = modules.hypernetworks.hypernetwork.Hypernetwork( + name=name, + enable_sizes=[int(x) for x in enable_sizes], + layer_structure=layer_structure, + activation_func=activation_func, + weight_init=weight_init, + add_layer_norm=add_layer_norm, + use_dropout=use_dropout, + ) + hypernet.save(fn) + + shared.reload_hypernetworks() + + return fn + def train_hypernetwork(hypernetwork_name, learn_rate, batch_size, gradient_step, data_root, log_directory, training_width, training_height, steps, shuffle_tags, tag_drop_out, latent_sampling_method, create_image_every, save_hypernetwork_every, template_file, preview_from_txt2img, preview_prompt, preview_negative_prompt, preview_steps, preview_sampler_index, preview_cfg_scale, preview_seed, preview_width, preview_height): # images allows training previews to have infotext. Importing it at the top causes a circular import problem. diff --git a/modules/hypernetworks/ui.py b/modules/hypernetworks/ui.py index c2d4b51c..e7f9e593 100644 --- a/modules/hypernetworks/ui.py +++ b/modules/hypernetworks/ui.py @@ -3,39 +3,16 @@ import os import re import gradio as gr -import modules.textual_inversion.preprocess -import modules.textual_inversion.textual_inversion +import modules.hypernetworks.hypernetwork from modules import devices, sd_hijack, shared -from modules.hypernetworks import hypernetwork not_available = ["hardswish", "multiheadattention"] -keys = list(x for x in hypernetwork.HypernetworkModule.activation_dict.keys() if x not in not_available) +keys = list(x for x in modules.hypernetworks.hypernetwork.HypernetworkModule.activation_dict.keys() if x not in not_available) def create_hypernetwork(name, enable_sizes, overwrite_old, layer_structure=None, activation_func=None, weight_init=None, add_layer_norm=False, use_dropout=False): - # Remove illegal characters from name. - name = "".join( x for x in name if (x.isalnum() or x in "._- ")) + filename = modules.hypernetworks.hypernetwork.create_hypernetwork(name, enable_sizes, overwrite_old, layer_structure, activation_func, weight_init, add_layer_norm, use_dropout) - fn = os.path.join(shared.cmd_opts.hypernetwork_dir, f"{name}.pt") - if not overwrite_old: - assert not os.path.exists(fn), f"file {fn} already exists" - - if type(layer_structure) == str: - layer_structure = [float(x.strip()) for x in layer_structure.split(",")] - - hypernet = modules.hypernetworks.hypernetwork.Hypernetwork( - name=name, - enable_sizes=[int(x) for x in enable_sizes], - layer_structure=layer_structure, - activation_func=activation_func, - weight_init=weight_init, - add_layer_norm=add_layer_norm, - use_dropout=use_dropout, - ) - hypernet.save(fn) - - shared.reload_hypernetworks() - - return gr.Dropdown.update(choices=sorted([x for x in shared.hypernetworks.keys()])), f"Created: {fn}", "" + return gr.Dropdown.update(choices=sorted([x for x in shared.hypernetworks.keys()])), f"Created: {filename}", "" def train_hypernetwork(*args): From f60c24f8121186f8d85f1096a96ddf685f625d04 Mon Sep 17 00:00:00 2001 From: eaglgenes101 Date: Sat, 24 Dec 2022 22:16:01 -0500 Subject: [PATCH 56/59] Add CSS classes for the settings panels --- modules/ui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ui.py b/modules/ui.py index 9dec61d5..65af8966 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -657,7 +657,7 @@ def create_ui(): setup_progressbar(progressbar, txt2img_preview, 'txt2img') with gr.Row().style(equal_height=False): - with gr.Column(variant='panel'): + with gr.Column(variant='panel', elem_id="txt2img_settings"): steps = gr.Slider(minimum=1, maximum=150, step=1, label="Sampling Steps", value=20) sampler_index = gr.Radio(label='Sampling method', elem_id="txt2img_sampling", choices=[x.name for x in samplers], value=samplers[0].name, type="index") @@ -812,7 +812,7 @@ def create_ui(): setup_progressbar(progressbar, img2img_preview, 'img2img') with gr.Row().style(equal_height=False): - with gr.Column(variant='panel'): + with gr.Column(variant='panel', elem_id="img2img_settings"): with gr.Tabs(elem_id="mode_img2img") as tabs_img2img_mode: with gr.TabItem('img2img', id='img2img'): From 61a273236ffd1366456cac7040e30972ca65dc2c Mon Sep 17 00:00:00 2001 From: Allen Benz Date: Sat, 24 Dec 2022 20:23:12 -0800 Subject: [PATCH 57/59] Fix clip interrogate from the webui A recent change made the image RGBA, which makes the clip interrogator unhappy. deepbooru and calling the interrogator from the api already do the conversion so this is the only place that needed it. --- modules/ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ui.py b/modules/ui.py index 9dec61d5..7bf5abd9 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -270,7 +270,7 @@ def apply_styles(prompt, prompt_neg, style1_name, style2_name): def interrogate(image): - prompt = shared.interrogator.interrogate(image) + prompt = shared.interrogator.interrogate(image.convert("RGB")) return gr_show(True) if prompt is None else prompt From 8eef9d8e782aa0655241e43f67059aa7bef3bdca Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sun, 25 Dec 2022 09:03:56 +0300 Subject: [PATCH 58/59] a way to add an exception to unpickler without explicitly calling load_with_extra --- modules/safe.py | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/modules/safe.py b/modules/safe.py index 479c8b86..ec23a53c 100644 --- a/modules/safe.py +++ b/modules/safe.py @@ -103,7 +103,7 @@ def check_pt(filename, extra_handler): def load(filename, *args, **kwargs): - return load_with_extra(filename, *args, **kwargs) + return load_with_extra(filename, extra_handler=global_extra_handler, *args, **kwargs) def load_with_extra(filename, extra_handler=None, *args, **kwargs): @@ -151,5 +151,42 @@ def load_with_extra(filename, extra_handler=None, *args, **kwargs): return unsafe_torch_load(filename, *args, **kwargs) +class Extra: + """ + A class for temporarily setting the global handler for when you can't explicitly call load_with_extra + (because it's not your code making the torch.load call). The intended use is like this: + +``` +import torch +from modules import safe + +def handler(module, name): + if module == 'torch' and name in ['float64', 'float16']: + return getattr(torch, name) + + return None + +with safe.Extra(handler): + x = torch.load('model.pt') +``` + """ + + def __init__(self, handler): + self.handler = handler + + def __enter__(self): + global global_extra_handler + + assert global_extra_handler is None, 'already inside an Extra() block' + global_extra_handler = self.handler + + def __exit__(self, exc_type, exc_val, exc_tb): + global global_extra_handler + + global_extra_handler = None + + unsafe_torch_load = torch.load torch.load = load +global_extra_handler = None + From c6f347b81f584b6c0d44af7a209983284dbb52d2 Mon Sep 17 00:00:00 2001 From: AUTOMATIC <16777216c@gmail.com> Date: Sun, 25 Dec 2022 09:47:24 +0300 Subject: [PATCH 59/59] fix ruined SD upscale --- scripts/sd_upscale.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/sd_upscale.py b/scripts/sd_upscale.py index 28bd96b3..e8c80a6c 100644 --- a/scripts/sd_upscale.py +++ b/scripts/sd_upscale.py @@ -35,8 +35,9 @@ class Script(scripts.Script): seed = p.seed init_img = p.init_images[0] + init_img = images.flatten(init_img, opts.img2img_background_color) - if (upscaler.name != "None"): + if upscaler.name != "None": img = upscaler.scaler.upscale(init_img, scale_factor, upscaler.data_path) else: img = init_img

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