Repository: mikeckennedy/python-switch (language: python)

Issue:
Post to PyPI
Looks like this is stable enough and has received enough feedback that I'm ready to start thinking about posting it to PyPI.

Hold tight for `pip install switchlang`


Discussion / hints:
# Please don't

For starters, it's not my intention to be mean or negative. I'm a huge fan of your work, @mikeckennedy  (with this small exception). [Talk python to me](https://talkpython.fm/) has been part of my daily/weekly Podcast diet for a while now and I'm a happy customer of your (and Matt Makai's) excellent [python for entrepenuers course](https://training.talkpython.fm/courses/details/python-for-entrepreneurs-build-and-launch-your-online-business). I think that by-and-large, the Python community is a lot better off for all your efforts and I really appreciate what you do.

Also, I don't want you to think I'm writing this because you accepted another pull request on this project that was philosophically mutually exclusive to mine. I knew it wouldn't make sense to merge both pull requests -- that's why I closed it myself.

*with that out of the way...*

# switch-lang considered harmful. why?

1. It is less functional than an equivalent if/elif statement, single-dispatch, or dictionary-based dispatch.
1. It is less functional than equivalent switch statements in other languages and doesn't "look" or behave like any of them.
1. This idea was reviewed and rejected in [PEP 3103](https://www.python.org/dev/peps/pep-3103/#rejection-notice) for good reasons.
1. It creates an abstraction over something that should be absolutely crystal-clear - **control-flow** - with no substantial benefit.
1. People will use it. Critical portions of someone's code will rely on a library that will need to be understood and maintained over time. Maintainers who would otherwise be able to immediately know what an equivalent if/elif statement does in the code will need to review the documentation for this library. (I would, anyway)
1. There exists [a more powerful library](https://github.com/tetrapus/switchcase) that addresses some of the shortcomings of switch-lang that the author created as a joke because it's a bad idea (by the author's own admission)

## Less functional than if/elif

    # old style
    var = 'foo'
    if True:
        var = 'bar'

    # switch-lang style
    with switch(True) as s:
        def set_var(arg):
            global var
            var = arg
        s.case(True, lambda: set_var('bar'))


## Less functional than other language implementations
A couple examples from swift

    let somePoint = (1, 1)
    switch somePoint {
    case (0, 0):
        print("\(somePoint) is at the origin")
    case (_, 0):
        print("\(somePoint) is on the x-axis")
    case (0, _):
        print("\(somePoint) is on the y-axis")
    case (-2...2, -2...2):
        print("\(somePoint) is inside the box")
    default:
        print("\(somePoint) is outside of the box")
    }


    let yetAnotherPoint = (1, -1)
    switch yetAnotherPoint {
    case let (x, y) where x == y:
        print("(\(x), \(y)) is on the line x == y")
    case let (x, y) where x == -y:
        print("(\(x), \(y)) is on the line x == -y")
    case let (x, y):
        print("(\(x), \(y)) is just some arbitrary point")
    }

Not only do we have variable-binding, tuple-unpacking, and pattern-matching -- the cases have access to the switch statement's namespace and can have multiple statements without needing to resort to defining a function separately. There exists *no* implementation of switch statements in *any other language* where you can't have multiple statements in a case without defining a function in a higher scope -- and where those cases don't otherwise have access to the local scope they're executed in.

## This idea was already rejected in PEP 3103

Much of the discussion around a potential switch statement in the language revolved around dispatching on values that weren't hashable. This library doesn't address the issue of what to do in the event that the value being dispatched on isn't hashable.

## Obfuscating control-flow
If I'm debugging someone's code and this library is being called at some point in the stacktrace, I'm going to have to go look up what it does, because I'm not going to trust someone's personal implementation of a switch. I wouldn't need to do that if it were just an if/elif or another construct native to Python and/or its stdlib.

## [Switchcase](https://github.com/tetrapus/switchcase) is better
No need to define functions for multi-statement cases and access to the local namespace. What more is there to say?

---

I think this is a good library to show off a potential use of context managers, but I would never recommend someone actually use it. I don't think it's terrible to have on github just as an interesting demo/idea, but my fear is that having it on pypi will encourage people to use it in the real-world.
Hey @mikeckennedy, do we have any updates on this issue?
Hi @vyahello I'm still partial to making it a proper package. But haven't done so for two reasons. First, I think I'd like to polish it a bit more. Second, I don't have the time to write the docs and maintain it much right now. So I guess consider it paused but not closed?
okay, that's fine, let it be paused then, I was wondering about package because I want to use it in my own projects, anyway I guess I can just clone your repo to use source code for now.

@mikeckennedy  BTW thanks for making it, I hope it will be a builtin feature for a future python release :)
You're welcome! Yes, it's just one python file so for now I've been copying it into my various projects to use it.

Fix patch:
diff --git a/README.md b/README.md
index 54ef8a9..5edde27 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,10 @@
-# python-switch
+# switchlang
+[![](https://img.shields.io/badge/python-3.6+-blue.svg)](https://www.python.org/downloads/) 
+[![](https://img.shields.io/pypi/l/markdown-subtemplate.svg)](https://github.com/mikeckennedy/python-switch/blob/master/LICENSE)
+[![](https://img.shields.io/pypi/dm/python-switch.svg)](https://test.pypi.org/project/switchlang/)
 
-Adds switch blocks to Python.
+
+Adds switch blocks to the Python language.
 
 This module adds explicit switch functionality to Python 
 without changing the language. It builds upon a standard
@@ -29,6 +33,14 @@ def process_with_data(*value):
     print("Found with data: {}".format(value))
 ``` 
 
+## Installation
+
+Simply install via pip:
+
+```bash
+pip install switchlang
+```
+
 ## Features
 
 * More explicit than using dictionaries with functions as values.
diff --git a/requirements-dev.txt b/requirements-dev.txt
new file mode 100644
index 0000000..0aa42e0
--- /dev/null
+++ b/requirements-dev.txt
@@ -0,0 +1,2 @@
+pytest
+# twine
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..bf5b7de
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,54 @@
+import io
+import os
+import re
+
+from setuptools import find_packages
+from setuptools import setup
+
+
+def read(filename):
+    filename = os.path.join(os.path.dirname(__file__), filename)
+    text_type = type(u"")
+    with io.open(filename, mode="r", encoding='utf-8') as fd:
+        return re.sub(text_type(r':[a-z]+:`~?(.*?)`'), text_type(r'``\1``'), fd.read())
+
+
+def read_version():
+    filename = os.path.join(os.path.dirname(__file__), 'switchlang', '__init__.py')
+    with open(filename, mode="r", encoding='utf-8') as fin:
+        for line in fin:
+            if line and line.strip() and line.startswith('__version__'):
+                return line.split('=')[1].strip().strip("'")
+
+    return "0.0.0.0"
+
+
+requires = []
+
+setup(
+    name="switchlang",
+    version=read_version(),
+    url="https://github.com/mikeckennedy/python-switch",
+    license='MIT',
+
+    author="Michael Kennedy",
+    author_email="michael@talkpython.fm",
+
+    description="Adds switch blocks to the Python language.",
+    long_description=read("README.md"),
+    long_description_content_type='text/markdown',
+
+    packages=find_packages(exclude=('tests',)),
+
+    install_requires=requires,
+
+    classifiers=[
+        'Development Status :: 4 - Beta',
+        'License :: OSI Approved :: MIT License',
+        'Programming Language :: Python',
+        'Programming Language :: Python :: 3',
+        'Programming Language :: Python :: 3.6',
+        'Programming Language :: Python :: 3.7',
+        'Programming Language :: Python :: 3.8',
+    ],
+)
diff --git a/switchlang/__init__.py b/switchlang/__init__.py
new file mode 100644
index 0000000..18d146f
--- /dev/null
+++ b/switchlang/__init__.py
@@ -0,0 +1,10 @@
+"""
+switchlang - Adds switch blocks to Python
+"""
+
+__version__ = '0.1.1'
+__author__ = 'Michael Kennedy <michael@talkpython.fm>'
+__all__ = ['switch', 'closed_range']
+
+from .__switchlang_impl import switch
+from .__switchlang_impl import closed_range
diff --git a/switchlang.py b/switchlang/__switchlang_impl.py
similarity index 96%
rename from switchlang.py
rename to switchlang/__switchlang_impl.py
index a41d52f..bd124d9 100644
--- a/switchlang.py
+++ b/switchlang/__switchlang_impl.py
@@ -1,5 +1,5 @@
 import uuid
-from typing import Callable, Any
+from typing import Callable, Any, Optional
 
 
 class switch:
@@ -33,7 +33,7 @@ def default(self, func: Callable[[], Any]):
         """
         self.case(switch.__default, func)
 
-    def case(self, key, func: Callable[[], Any], fallthrough=False):
+    def case(self, key, func: Callable[[], Any], fallthrough: Optional[bool] = False):
         """
             Specify a case for the switch block:
 


Test patch:
diff --git a/tests/coretests.py b/tests/test_core.py
similarity index 100%
rename from tests/coretests.py
rename to tests/test_core.py