Subscribe: Ned Batchelder's blog
http://www.nedbatchelder.com/blog/rss.xml
Added By: Feedage Forager Feedage Grade B rated
Language: English
Tags:
abc abc  abc  batt  def  eighths  fibonacci  ironpython  level  line  page  python  return  rules  str  test  text  unicode 
Rate this Feed
Rate this feedRate this feedRate this feedRate this feedRate this feed
Rate this feed 1 starRate this feed 2 starRate this feed 3 starRate this feed 4 starRate this feed 5 star

Comments (0)

Feed Details and Statistics Feed Statistics
Preview: Ned Batchelder's blog

Ned Batchelder's blog



Ned Batchelder's personal blog.



 



Triangular Fibonacci numbers

2017-06-17T11:56:40-05:00

Yesterday in my post about 55, I repeated Wikipedia's claim that 55 is the largest number that is both triangular and in the Fibonacci sequence. Chris Emerson commented to ask for a proof. After a moment's thought, I realized I had no idea how to prove it.The proof is in On Triangular Fibonacci Numbers, a dense 10-page excursion into number theory I don't understand.While I couldn't follow the proof, I can partially test the claim empirically, which leads to fun with Python and itertools, something which is much more in my wheelhouse.I started by defining generators for triangular numbers and Fibonacci numbers:def tri():     """Generate an infinite sequence of triangular numbers."""     n = 0     for i in itertools.count(start=1):         n += i         yield n          print(list(itertools.islice(tri(), 50))) [1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 91, 105, 120, 136, 153, 171, 190, 210, 231, 253, 276, 300, 325, 351, 378, 406, 435, 465, 496, 528, 561, 595, 630, 666, 703, 741, 780, 820, 861, 903, 946, 990, 1035, 1081, 1128, 1176, 1225, 1275]def fib():     """Generate an infinite sequence of Fibonacci numbers."""     a, b = 1, 1     while True:         yield a         b, a = a, a+b          print(list(itertools.islice(fib(), 50))) [1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584,  4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811,  514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352,  24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437,  701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049,  12586269025, 20365011074] The Fibonacci sequence grows much faster!My first impulse was to make two sets of the numbers in the sequences, and intersect them, but building a very large set took too long. So instead I wrote a function that took advantage of the ever-increasing nature of the sequences to look for equal elements in two monotonic sequences:def find_same(s1, s2):     """Find equal elements in two monotonic sequences."""     try:         i1, i2 = iter(s1), iter(s2)         n1, n2 = next(i1), next(i2)         while True:             while n1 < n2:                 n1 = next(i1)             if n1 == n2:                 yield n1                 n1 = next(i1)             while n2 < n1:        [...]



Math factoid of the day: 55

2017-06-16T06:33:00-05:00

55 is in the Fibonacci sequence:

1 1 2 3 5 8 13 21 34 55 ...

55 is a triangular number:

1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 = 55

It is the largest number that is both Fibonacci and triangular.

It is also a Kaprekar number:

55² = 3025 and 30+25 = 55




Re-ruling .rst

2017-05-12T07:26:34-05:00

Sometimes, you need a small job done, and you can write a small Python program, and it does just what you need, and it pleases you. I have some Markdown files to convert to ReStructured Text. Pandoc does a really good job. But it chooses a different order for heading punctuation than our house style, and I didn't see a way to control it.But it was easy to write a small thing to do the small thing:import re import sys # The order we want our heading rules. GOOD_RULES = '#*=-.~' # A rule is any line of all the same non-word character, 3 or more. RULE_RX = r"^([^\w\d])\1\1+$" def rerule_file(f):     rules = {}     for line in f:         line = line.rstrip()         rule_m = re.search(RULE_RX, line)         if rule_m:             if line[0] not in rules:                 rules[line[0]] = GOOD_RULES[len(rules)]             line = rules[line[0]] * len(line)         print(line) rerule_file(sys.stdin) If you aren't conversant in .rst: there's no fixed order to which punctuation means which level heading. The first rule encountered is heading 1, the next style found is heading 2, and so on.There might be other ways to do this, but this makes me happy. [...]



Shell = Maybe

2017-04-24T10:38:57-05:00

A common help Python question: how do I get Python to run this complicated command line program? Often, the answer involves details of how shells work. I tried my hand at explaining it what a shell does, why you want to avoid them, how to avoid them from Python, and why you might want to use one: Shell = Maybe.




Text-mode menu bar indicators

2017-04-17T09:44:24-05:00

I recently upgraded my Mac operating system, and decided to try out a new feature: automatically hiding the menu bar. This gives me back another sliver of vertical space. But it has a drawback: I no longer have the time, battery life, and speaker volume indicators available at a glance.I went looking for a thing that I figured must exist: a Mac app that would display that information in a dock icon. I already have a dock clock. I found a dock battery indicator, though it tried so hard to be cute and pictorial, I couldn't tell what it was telling me.Asking around, I got a recommendation for GeekTool. It lets you draw a panel on your desktop, and then draw in the panel with the output of a script. Now the ball was back in my court: I could build my own thing.I'd long ago moved the dock to the left side of the screen (again, to use all the vertical space for my own stuff.) This left a small rectangle of desktop visible at the upper left and lower left, even with maximized windows. I drew a panel in the upper left of the desktop, and set it to run this script every five seconds:#!/usr/bin/env python3.6 import datetime import re import subprocess def block_eighths(eighths):     """Return the Unicode string for a block of so many eighths."""     assert 0 <= eighths <= 8     if eighths == 0:         return "\u2003"     else:         return chr(0x2590 - eighths) def gauge(percent):     """Return a two-char string drawing a 16-part gauge."""     slices = round(percent / (100 / 16))     b1 = block_eighths(min(slices, 8))     b2 = block_eighths(max(slices - 8, 0))     return b1 + b2 now = datetime.datetime.now() print(f"{now:%-I:%M\n%-m/%-d}") batt = subprocess.check_output(["pmset", "-g", "batt"]).decode('utf8').splitlines() m = re.search(r"\d+%", batt[1]) if m:     level = m.group(0)     batt_percent = int(level[:-1]) else:     level = "??%" if "discharging" in batt[1]:     arrow = "\u25bc"        # BLACK DOWN-POINTING TRIANGLE elif "charging" in batt[1]:     arrow = "\u25b3"        # WHITE UP-POINTING TRIANGLE else:     arrow = "" print(level + arrow) print(gauge(batt_percent) + "\u2578")   # BOX DRAWINGS HEAVY LEFT vol = subprocess.check_output(["osascript", "-e", "get volume settings"]).decode('utf8') m = re.search(r"^output volume:(\d+), .* muted:(\w+)", vol) if m:     level, muted = m.groups()     if muted == 'true':         level = "\u20e5"        # COMBINING REVERSE [...]



Clean-text bookmarklet

2017-04-08T22:34:31-05:00

I love the text-based web. I love that people can speak their minds, express opinions, encourage each other, and create a lively world of words. This also means they are free to design their text in, shall we say, expressive ways. Those ways are not always ideal for actually reading the words.Today I really liked Tiberius Hefflin's Part of That World, about the need to recognize non-code contributions in open source projects. You should read it, it is good and true.But when I first got to the page, I saw this:To start with the positive, this text has an elegance to it. It gives a peaceful quiet impression. It pairs perfectly with the mermaid illustration on the page. But I find it hard to read. This typeface is too weak to be light-on-dark, and letterspacing is almost always a bad idea for body text. It isn't even white-on-black, it's 70% white on black, so the letters seem to be hiding in the dark.I don't mean to pick on this page. It's a well-designed page. There's clearly a mood being created here, and it's been established well. There are many pages online that veer much farther from the usual than this.My solution for pages like this is a bookmarklet to strip away idiosyncracies in text layout. It changes text to almost-black on white, it removes letterspacing and shadows, and changes full-justified text to left-justified. When I use the bookmarklet on Part of That World, it looks like this:You might prefer the original. That's fine, to each their own. You might feel like the personality has been bleached from this text. To some extent, that's true. But I saw the original, and can choose between them. This helped me to read the words, and not get snagged on the design of the page.This is the bookmarklet: Clean text.This is the JavaScript code in the bookmarklet, formatted and tweaked so you can read it:javascript:(function () {     var newSS = document.createElement('link'),         styles = (             '* { ' +                 'background: #fff; color: #111; ' +                 'letter-spacing: 0; text-shadow: none; hyphens: none; ' +             '}' +             ':link, :link * { color: #0000EE; } ' +             ':visited, :visited * { color: #551A8B; }'         ).replace(/;/g,' !important;');     newSS.rel = 'stylesheet';     newSS.href = 'data:text/css,' + escape(styles);     document.getElementsByTagName('head')[0].appendChild(newSS);     var els = document.getElementsByTagName('*');     for (var i = 0, el; el = els[i]; i++) {         if (getComputedStyle(el).textAlign === 'justify') {             el.style.textAlign = 'left';         }     } })(); There are other solutions to ecc[...]



IronPython is weird

2017-03-15T07:45:06-05:00

Have you fully understood how Python 2 and Python 3 deal with bytes and Unicode? Have you watched Pragmatic Unicode (also known as the Unicode Sandwich, or unipain) forwards and backwards? You're a Unicode expert! Nothing surprises you any more.Until you try IronPython...Turns out IronPython 2.7.7 has str as unicode!C:\Users\Ned>"\Program Files\IronPython 2.7\ipy.exe" IronPython 2.7.7 (2.7.7.0) on .NET 4.0.30319.42000 (32-bit) Type "help", "copyright", "credits" or "license" for more information. >>> "abc" 'abc' >>> type("abc") >>> u"abc" 'abc' >>> type(u"abc") >>> str is unicode True >>> str is bytes False String literals work kind of like they do in Python 2: \u escapes are recognized in u"" strings, but not "" strings, but they both produce the same type:>>> "abc\u1234" 'abc\\u1234' >>> u"abc\u1234" u'abc\u1234' Notice that the repr of this str/unicode type will use a u-prefix if any character is non-ASCII, but it the string is all ASCII, then the prefix is omitted.OK, so how do we get a true byte string? I guess we could encode a unicode string? WRONG. Encoding a unicode string produces another unicode string with the encoded byte values as code points!:>>> u"abc\u1234".encode("utf8") u'abc\xe1\x88\xb4' >>> type(_) Surely we could at least read the bytes from a file with mode "rb"? WRONG.>>> type(open("foo.py", "rb").read()) >>> type(open("foo.py", "rb").read()) is unicode True On top of all this, I couldn't find docs that explain that this happens. The IronPython docs just say, "Since IronPython is a implementation of Python 2.7, any Python documentation is useful when using IronPython," and then links to the python.org documentation.A decade-old article on InfoQ, The IronPython, Unicode, and Fragmentation Debate, discusses this decision, and points out correctly that it's due to needing to mesh well with the underlying .NET semantics. It seems very odd not to have documented it some place. Getting coverage.py working even minimally on IronPython was an afternoon's work of discovering each of these oddnesses empirically.Also, that article quotes Guido van Rossum (from a comment on Calvin Spealman's blog):You realize that Jython has exactly the same str==unicode issue, right? I've endorsed this approach for both versions from the start. So I don't know what you are so bent out of shape about.I guess things have changed with Jython in the intervening ten years, because it doesn't behave that way now:$ jython Jython 2.7.1b3 (default:df42d5d6be04, Feb 3 2016, 03:22:46) [Java HotSpot(TM) 64-Bit Server VM (Oracle Corporation)] on java1.8.0_31 Type "help", "copyright", "credits" or "license" for more information. >>> 'abc' 'abc' >>> type(_) >>> str is unicode False >>> type("abc") >>> type(u"abc") >>> u"abc".enco[...]



Rubik's algorithms

2017-02-26T19:43:54-06:00

Recently, a nephew asked about how to solve a Rubik's Cube. I couldn't sit down with him to show him what I knew, so I looked around the web for explanations. I was surprised by two things: first, that all the pages offering solutions seemed to offer the same one, even down to the colors discussed: "Start by making a white cross, ..., finally, finish the yellow side."

Second, that the techniques (or "algorithms") were often given without explanation. They're presented as something to memorize.

My own solving technique uses a few algorithms constructed in a certain way that I describe in Two-Part Rubik's Algorithms. I wrote them up as a resource I hope my nephew will be able to use.

(image)

BTW, that page makes use of Conrad Rider's impressive TwistySim library.




Https

2017-02-25T17:37:11-06:00

Someone posted a link to my latest blog post on /r/Python, but somehow got an https link for it. That's odd: my site doesn't even properly serve content over https. People were confused by the broken link.

I should say, my site didn't even serve content over https, because now it does. I'd been meaning to enable https, and force its use, for a long time. This broken link pushed it to the top of the list.

Let's Encrypt is the certificate authority of choice these days, because they are free and automatable. And people say they make it easy, but I have to say, I would not have classified this as easy. I'm sure it's easier than it used to be, but it's still a confusing maze of choices, with decision points you are expected to navigate.

Actually getting everything installed requires sudo, or without sudo, using third-party tools, with instructions from obscure blog posts. There's clearly still room for improvement.

Once you have the certificate in place, you need to redirect your http site to https. Then you have to fix the http references in your site. Protocol-relative (or schema-less) URLs are handy here.

It's all done now, the entire site should always be https. I'm glad I finally got the kick in the pants to do it. If you find something wrong, let me know.




A tale of two exceptions, continued

2017-02-23T06:36:00-06:00

In my last blog post, A tale of two exceptions, I laid out the long drawn-out process of trying to get a certain exception to make tests skip in my test runner. I ended on a solution I liked at the time.But it still meant having test-specific code in the product code, even if it was only a single line to set a base class for an exception. It didn't feel right to say "SkipTest" in the product code, even once.In that blog post, I said,One of the reasons I write this stuff down is because I'm hoping to get feedback that will improve my solution, or advance my understanding. ... a reader might object and say, "you should blah blah blah."Sure enough, Ionel said,A better way is to handle this in coverage's test suite. Possible solution: wrap all your tests in a decorator that reraises with a SkipException.I liked this idea. The need was definitely a testing need, so it should be handled in the tests. First I tried doing something with pytest to get it to do the conversion of exceptions for me. But I couldn't find a way to make it work.So: how to decorate all my tests? The decorator itself is fairly simple. Just call the method with all the arguments, and return its value, but if it raises StopEverything, then raise SkipTest instead:def convert_skip_exceptions(method):     """A decorator for test methods to convert StopEverything to SkipTest."""     def wrapper(*args, **kwargs):         """Run the test method, and convert exceptions."""         try:             result = method(*args, **kwargs)         except StopEverything:             raise unittest.SkipTest("StopEverything!")         return result     return wrapper But decorating all the test methods would mean adding a @convert_skip_exceptions line to hundreds of test methods, which I clearly was not going to do. I could use a class decorator, which meant I would only have to add a decorator line to dozens of classes. That also felt like too much to do and remember to do in the future when I write new test classes.It's not often I say this, but: it was time for a metaclass. Metaclasses are one of the darkest magics Python has, and they can be mysterious. At heart, they are simple, but in a place you don't normally think to look. Just as a class is used to make objects, a metaclass is used to make classes. Since there's something I want to do everytime I make a new class (decorate its methods), a metaclass gives me the tools to do it.class SkipConvertingMetaclass(type):     """Decorate all test methods to convert StopEverything to SkipTest."""     def __new__(mcs, name, bases, attrs):         for attr_name, attr_value in attrs.items():             right_name = attr_name.startswith('test_')             right_type = isinstance(attr_value, types.FunctionType)             [...]