21

The section below goes into more detail, but basically someone stated that the Ruby-written DSL RSpec couldn't be rewritten in Python. Is that true? If so, why?

I'm wanting to better understand the technical differences between Ruby and Python.

Update: Why am I asking this question?

The Running away from RSpec discussion has some statements about it being "impossible" to recreate RSpec in Python. I was trying to make the question a little broader in hopes of learning more of the technical differences between Ruby and Python. In hindsight, maybe I should have tightened the question's scope to just asking if it truly is impossible to recreate RSpec in Python, and if so why.

Below are just a few quotes from the Running away from RSpec discussion.

Initial Question

For the past few weeks I have been thinking a lot about RSpec and why there is no clear, definite answer when someone asks:

"I'm looking for a Python equivalent of RSpec. Where can I find such a thing?"

Probably the most common (and understandable) answer is that Python syntax wouldn't allow such a thing whereas in Ruby it is possible.

First Response to Initial Question

Not syntax exactly. Rspec monkeypatches every object inside of its scope, inserting the methods "should" and "should_not". You can do something in python, but you can't monkeypatch the built-in types.

Another Response

As you suggest, it's impossible. Mote and PySpec are just fancy ways to name your tests: weak implementations of one tiny corner of RSpec. Mote uses horrible settrace magic; PySpec adds a bunch of domain-irrelevant noise. Neither even supports arbitrary context strings. RSpec is more terse, more expressive, removes the noise, and is an entirely reasonable thing to build in Ruby.

That last point is important: it's not just that RSpec is possible in Ruby; it's actually idiomatic.

Matthew Rankin
  • 457,139
  • 39
  • 126
  • 163
  • This is mostly a community movement, the Ruby community is much more inclined to this than the others. – Maurício Linhares Aug 16 '11 at 14:36
  • 3
    Not quite sure how you arrive at that assessment that more DSLs are written in Ruby? – Femi Aug 16 '11 at 14:38
  • Ruby has a much lighter syntax that makes it favorable, but that's just a matter of style. – tadman Aug 16 '11 at 14:45
  • 3
    How sad this question was closed... There are various constructive answers to it. – brandizzi Aug 16 '11 at 14:54
  • 5
    IMO it's almost entirely due to the syntax of Ruby, specifically call-without-parentheses and blocks. The "exact same" DSL in Python and Ruby would be less easily readable in Python. However, DSLs are not the answer to every problem, and "Everything is a DSL" is an antipattern. – Russell Borogove Aug 16 '11 at 17:45
  • Assumes facts not in evidence. – dmckee --- ex-moderator kitten Aug 17 '11 at 16:28
  • 1
    I was hoping this question would shed some light on the technical differences between Ruby and Python. Specifically, I was interested in a few comments in the "Running away from RSpec" discussion at http://permalink.gmane.org/gmane.comp.python.testing.general/3730 such as "Probably the most common (and understandable) answer is that Python syntax wouldn't allow such a thing whereas in Ruby it is possible." I have only empirical evidence to suggest there are more DSLs in Ruby, but I tried to make that clear by stating there "at least seem to be" more Ruby DSLs. – Matthew Rankin Aug 17 '11 at 23:31
  • Isn't the possibility of reopening the classes important at all in this? – finiteautomata Jan 21 '14 at 18:57

5 Answers5

10

If I had to point out one great difficulty for creating a Python RSpec, it would be the lack of a good syntax in Python for creating anonymous functions (as in JavaScript) or blocks (as in Ruby). The only option for a Python programmer is to use lambdas, which is not an option at all because lambdas just accept one expression. The do ... end blocks used in RSpec would have to be written as a function before calling describe and it, as in the example below:

def should_do_stuff():
    # ...
it("should do stuff", should_do_stuff)

Not so sexy, right?

There are some difficulties in creating the should methods, but I bet it would be a smaller problem. Actually, one does not even need to use such an unusual syntax—you could get similar results (maybe even better, depending on your taste) using the Jasmine syntax, which can be trivially implemented.

That said, I feel that Python syntax is more focused on efficiently representing the usual program components such as classes, functions, variables, etc. It is not well suited to be extended. I, for one, think that a good Python program is one where I can see objects, and functions, and variables, and I understand what each one of these elements do. Ruby programmers, OTOH, seem to seek for a more prose-like style, where a new language is defined for a new problem. It is a good way of doing things, too, but not a Pythonic way. Python is good to represent algorithms, not prose.

Sometimes it is a draconian limit. How could one use BDD for example? Well, the usual way of pushing these limits in Python is to effectively write your own DSL, but it should REALLY be another language. That is what Pyccuracy is, for example: another language for BDD. A more mainstream example is doctest. (Actually, if I would write some BDD Python library, I would write it based on doctest.) Another example of Python DSL is Twill. And yet another example is reStructuredText, used in Sphinx.

Summarizing: IMHO the hardest barrier to DSLs in Python is the lack of a flexible syntax for creating anonymous functions. And it is not a fault*: Python is not fond of having its syntax heavily explored anyway—it is considered to make code less clear in the Python universe. If you want a new syntax in Python you are well advised to write your own language, or at least it is the way I feel.

* Or maybe it is - I have to confess that I miss anonymous functions. However, I recognize that they would be hard to implement elegantly given the Python semantic indentation.

brandizzi
  • 26,083
  • 8
  • 103
  • 158
  • Agree, it's a matter of preference and a habit. Python has doctest and unittest. Python isn't about sexy and prose :) It's about clarity and efficiency. – Kee Aug 24 '11 at 03:34
  • @brandizzi: Can you explain your statements "Well, the usual way of pushing these limits in Python is to effectively write your own DSL, but it should REALLY be another language. That is what Pyccuracy is, for example: another language for BDD." Why wouldn't Pyccuracy be considered a DSL written in Python, similar to RSpec being a DSL written in Ruby? According to the Pyccuracy wiki on Github (https://github.com/heynemann/pyccuracy/wiki/Overview), it is a DSL. – Matthew Rankin Aug 25 '11 at 03:19
  • 3
    @Matthew succinctly Pyccuracy is a DSL (a domain-specific language, that is, a language to solve a single kinde of problems), but it is not valid Python code. The Pyccuracy syntax is not the Python syntax. OTOH, RSpec is a DSL but is also pure Ruby: it is just a way of using the own Ruby syntax for BDD. – brandizzi Aug 25 '11 at 13:06
  • 1
    There is the `with` keyword, though. – jupp0r Mar 27 '12 at 16:56
  • 1
    @jupp fairly true, but I have yet to see really innovative uses of it. However, I agree that the _potential_ is there. – brandizzi Mar 27 '12 at 21:01
  • @brandizzi true, but it does provide for ruby-like yield magic, although it is of course distracting the DSL flow. I think it should be (pun intended) not that difficult to write a descriptive test framework employing syntax like `with it("should write data to stdout")`. Think of it as an unnecessary distraction like the `do` keyword in ruby. – jupp0r Mar 28 '12 at 00:25
  • @jupp0r it is not like Ruby also because it does evaluate the code inside `with` immediately, while the blocks passed to functions in Ruby can be evaluated in the future. Nonetheless, I decided to [try it in the context of our debate](https://bitbucket.org/brandizzi/testwith) and it can be still fun :) – brandizzi Oct 04 '12 at 12:08
5

One of Ruby's strengths is in the creation of DSLs. However the reasons given for it being difficult in python can be sidestepped. For example you can easily subclass the builtin types, e.g:

>>> class myint(int):  pass
>>> i = myint(5)
>>> i
5

If I were going to create a DSL in python I'd use pyparsing or Parsley and something like the above behind the scenes, optimizing the syntax for the problem, not the implementation language.

Gringo Suave
  • 29,931
  • 6
  • 88
  • 75
5

I set out on an attempt to implement something like rspec in Python.

I got this:

with It('should pass') as test:
    test.should_be_equal(1, 1)

source: https://gist.github.com/2029866

(thoughts?)

EDIT: My answer to your question is that the lack of anonymous blocks prevents a Ruby DSL like RSpec from being rewritten in Python but you can get a close approximation using with statements.

Austin Richardson
  • 8,078
  • 13
  • 43
  • 49
2

By mixing Mamba and Expects, I think you can get very close to what RSpec is for Rails...
https://github.com/nestorsalceda/mamba
https://github.com/jaimegildesagredo/expects

Also, I think Specter should match your expectations with testing:
https://github.com/jmvrbanac/Specter
http://specter.readthedocs.io/en/latest/writing_tests/index.html

1

I think this is what you are looking for. Yes, we made the "impossible" in python "sure" is an utility belt for expressive python tests, created by Gabriel Falcão

Gabriel Falcão
  • 1,075
  • 1
  • 13
  • 9