Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #91374

Re: should "self" be changed?

From Marko Rauhamaa <marko@pacujo.net>
Newsgroups comp.lang.python
Subject Re: should "self" be changed?
Date 2015-05-28 18:01 +0300
Organization A noiseless patient Spider
Message-ID <87siagagpx.fsf@elektro.pacujo.net> (permalink)
References <551c8229-f426-45f0-a0ee-fdad1b161f59@googlegroups.com> <877frvf7f2.fsf@elektro.pacujo.net> <9f9498a5-8291-4347-aef2-ada324bb47a7@googlegroups.com> <87r3q3dqju.fsf@elektro.pacujo.net> <vg3k2vsvlrk.fsf@coffee.modeemi.fi>

Show all headers | View raw


Anssi Saari <as@sci.fi>:

> Do you have an example of state pattern using nested classes and
> python? With a quick look I didn't happen to find one in any language.

Here's an sampling from my mail server:

========================================================================
class SMTPServerConnection(ServerConnection):
    #:     :     :
    #:     :     :

    def __init__(self, server, sock, domain, peer):
        super().__init__(server, sock)
        conn = self
        client_ip = peer[0]

        class STATE:
            def __str__(self):
                return self.__class__.__name__
            def handle_command(self, cmd):
                conn.log("handle_command (unexpected): {}".format(cmd))
                assert False
            def handle_bad_command(self, reason):
                conn.log("handle_bad_command (unexpected): {}".format(reason))
                assert False
            def handle_eof(self):
                conn.log("eof (unexpected)")
                assert False
            def terminate(self):
                assert False
        
        class IDLE(STATE):
            def handle_command(self, cmd):
                conn.log("handle_command: {}".format(cmd))
                verb, args = conn.split_cmd(cmd)
                if verb == b'EHLO':
                    self.process_ehlo_or_helo(
                        args,
                        "PIPELINING",
                        "SIZE {:d}".format(conn.MAX_MSG_SIZE),
                        "VRFY",
                        "EXPN",
                        "8BITMIME")
                elif verb == b'HELO':
                    self.process_ehlo_or_helo(args)
                elif verb in [ b'NOOP', b'RSET' ]:
                    conn.respond(250, "OK")
                elif verb == b'HELP':
                    conn.respond(214, "No help available")
                elif verb in [ b'VRFY', b'EXPN' ]:
                    conn.respond(550, "Access denied to you")
                elif verb == b'QUIT':
                    conn.respond(221, "{} Closing channel".format(
                            server.domain))
                    conn.shut_down()
                    conn.set_state(QUITTING)
                elif verb == b'MAIL':
                    self.handle_mail(args)
                elif verb in [ b'RCPT', b'DATA' ]:
                    conn.respond(503, "Bad sequence of commands")
                else:
                    conn.respond(500, "Command unrecognized")

            def process_ehlo_or_helo(self, args, *capabilities):
                if not args:
                    conn.respond(501, "Missing parameter")
                    return
                try:
                    # may be an IP address
                    conn.helo_domain_name = args[0].decode()
                except UnicodeError:
                    conn.respond(501, "Bad encoding in parameter")
                    return
                if conn.local_client:
                    conn.respond(250, server.domain, *capabilities)
                    conn.set_state(IDLE)
                    return
                ## todo: remove the "suspend" concept from mux
                conn.suspend()
                conn.log("authorize helo {} from {}".format(
                        conn.helo_domain_name, client_ip))
                conn.set_state(SPF_HELO)
                def callback(verdict, reason):
                    conn.state.handle_spf_verdict(
                        verdict, reason, *capabilities)
                conn.spf_query = server.spf_client.authorize_helo(
                    server.host, conn.helo_domain_name, server.family,
                    client_ip, callback, xid = id(conn))

            #:     :     :
            #:     :     :

        class SPF_HELO(STATE):
            def terminate(self):
                conn.resume()
                conn.spf_query.cancel()
                conn.close()
                if conn.timer is not None:
                    conn.timer.cancel()
                    conn.timer = None
                conn.set_state(ZOMBIE)

            def handle_spf_verdict(self, verdict, reason, *capabilities):
                conn.resume()
                conn.log("verdict {} reason {}".format(verdict, reason))
                # RFC 4408 §2.5.5 calls for leniency for SoftFail
                #if verdict in [ SPFClient.FAIL, SPFClient.SOFT_FAIL ]:
                if verdict in [ SPFClient.FAIL ]:
                    conn.respond(550, "SPF check failed: {}".format(reason))
                    conn.set_state(IDLE)
                    return
                conn.respond(250, server.domain, *capabilities)
                conn.set_state(IDLE)

        class SPF_SENDER(STATE):
            def terminate(self):
                conn.resume()
                conn.spf_query.cancel()
                conn.close()
                if conn.timer is not None:
                    conn.timer.cancel()
                    conn.timer = None
                conn.set_state(ZOMBIE)

            def handle_spf_verdict(self, verdict, reason):
                conn.resume()
                conn.log("verdict {} reason {}".format(verdict, reason))
                # RFC 4408 §2.5.5 calls for leniency for SoftFail
                #if verdict in [ SPFClient.FAIL, SPFClient.SOFT_FAIL ]:
                if verdict in [ SPFClient.FAIL ]:
                    conn.respond(550, "SPF check failed: {}".format(reason))
                    conn.set_state(IDLE)
                    return
                conn.spf_sender_verdict = verdict
                conn.spf_sender_reason = reason
                conn.respond(250, "OK")
                conn.set_state(RECIPIENTS)

        class RECIPIENTS(STATE):
            def handle_command(self, cmd):
                conn.log("handle_command: {}".format(cmd))
                verb, args = conn.split_cmd(cmd)
                if verb in [ b'EHLO', b'HELO', b'MAIL' ]:
                    conn.respond(503, "Bad sequence of commands")
                elif verb == b'NOOP':
                    conn.respond(250, "OK")
                elif verb == b'HELP':
                    conn.respond(214, "No help available")
                elif verb == b'VRFY' or verb == b'EXPN':
                    conn.respond(550, "Access denied to you")
                elif verb == b'RSET':
                    conn.set_state(IDLE)
                    conn.respond(250, "OK")
                elif verb == b'QUIT':
                    conn.respond(221, "{} Closing channel".format(
                            server.domain))
                    conn.shut_down()
                    conn.set_state(QUITTING)
                elif verb == b'RCPT':
                    self.handle_rcpt(args)
                elif verb == b'DATA':
                    self.handle_data(args)
                else:
                    conn.respond(500, "Command unrecognized")

            #:     :     :
            #:     :     :

        self.send(("220 {} Service ready\r\n".format(domain)).encode())
        self.timer = self.mux.after(self.SERVER_TIMEOUT, self.handle_timeout)
        #:     :     :
        #:     :     :
        self.state = IDLE()

    def set_state(self, state):
        new_state = state()
        self.log("set_state: {} -> {}".format(self.state, new_state))
        self.state = new_state

    def handle_command(self, cmd):
        self.state.handle_command(cmd)

    def handle_timeout(self):
        if self.timer is None:
            return
        self.timer = None
        self.log("timeout")
        self.state.terminate()

    def handle_spf_verdict(self, verdict, reason, *capabilities):
        self.state.handle_spf_verdict(verdict, reason, *capabilities)

    #:     :     :
    #:     :     :
========================================================================


Marko

Back to comp.lang.python | Previous | NextPrevious in thread | Next in thread | Find similar | Unroll thread


Thread

should "self" be changed? zipher <dreamingforward@gmail.com> - 2015-05-26 09:37 -0700
  Re: should "self" be changed? Laura Creighton <lac@openend.se> - 2015-05-26 18:57 +0200
    Re: should "self" be changed? zipher <dreamingforward@gmail.com> - 2015-05-26 20:01 -0700
    Re: should "self" be changed? zipher <dreamingforward@gmail.com> - 2015-05-26 20:15 -0700
  Re: should "self" be changed? Laurent Pointal <laurent.pointal@free.fr> - 2015-05-26 18:59 +0200
  Re: should "self" be changed? Mark Lawrence <breamoreboy@yahoo.co.uk> - 2015-05-26 18:28 +0100
    Re: should "self" be changed? zipher <dreamingforward@gmail.com> - 2015-05-26 19:48 -0700
      Re: should "self" be changed? zipher <dreamingforward@gmail.com> - 2015-05-26 20:17 -0700
        Re: should "self" be changed? Ben Finney <ben+python@benfinney.id.au> - 2015-05-27 14:39 +1000
          Re: should "self" be changed? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2015-05-27 15:47 +1000
            Re: should "self" be changed? Ben Finney <ben+python@benfinney.id.au> - 2015-05-27 21:29 +1000
              Re: should "self" be changed? zipher <dreamingforward@gmail.com> - 2015-05-27 05:40 -0700
                Re: should "self" be changed? Todd <toddrjen@gmail.com> - 2015-05-27 15:00 +0200
                Re: should "self" be changed? Grant Edwards <invalid@invalid.invalid> - 2015-05-27 14:19 +0000
                Re: should "self" be changed? zipher <dreamingforward@gmail.com> - 2015-05-30 16:18 -0700
            Re: should "self" be changed? zipher <dreamingforward@gmail.com> - 2015-05-30 16:10 -0700
          Re: should "self" be changed? zipher <dreamingforward@gmail.com> - 2015-05-30 16:13 -0700
  Re: should "self" be changed? Chris Angelico <rosuav@gmail.com> - 2015-05-27 03:31 +1000
  Re: should "self" be changed? random832@fastmail.us - 2015-05-26 14:11 -0400
  Re: should "self" be changed? Marko Rauhamaa <marko@pacujo.net> - 2015-05-26 22:46 +0300
    Re: should "self" be changed? Ned Batchelder <ned@nedbatchelder.com> - 2015-05-26 13:04 -0700
      Re: should "self" be changed? Marko Rauhamaa <marko@pacujo.net> - 2015-05-26 23:36 +0300
        Re: should "self" be changed? Anssi Saari <as@sci.fi> - 2015-05-28 17:07 +0300
          Re: should "self" be changed? Marko Rauhamaa <marko@pacujo.net> - 2015-05-28 18:01 +0300
            Re: should "self" be changed? Ian Kelly <ian.g.kelly@gmail.com> - 2015-05-28 09:40 -0600
              Re: should "self" be changed? Marko Rauhamaa <marko@pacujo.net> - 2015-05-28 19:59 +0300
                Re: should "self" be changed? Chris Angelico <rosuav@gmail.com> - 2015-05-29 03:06 +1000
                Re: should "self" be changed? zipher <dreamingforward@gmail.com> - 2015-05-30 16:39 -0700
            Re: should "self" be changed? Steven D'Aprano <steve@pearwood.info> - 2015-05-29 12:00 +1000
              Re: should "self" be changed? Steven D'Aprano <steve@pearwood.info> - 2015-05-29 15:46 +1000
              Re: should "self" be changed? Steven D'Aprano <steve@pearwood.info> - 2015-06-03 02:50 +1000
                Re: should "self" be changed? "Dr. BigCock" <dreamingforward@gmail.com> - 2015-06-02 10:16 -0700
                Re: should "self" be changed? Marko Rauhamaa <marko@pacujo.net> - 2015-06-02 20:19 +0300
                Re: should "self" be changed? "Dr. Bigcock" <dreamingforward@gmail.com> - 2015-06-02 11:02 -0700
                Re: should "self" be changed? Ian Kelly <ian.g.kelly@gmail.com> - 2015-06-02 19:39 -0600
                Re: should "self" be changed? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2015-06-03 17:05 +1000
    Re: should "self" be changed? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2015-05-27 15:20 +1000
  Re: should "self" be changed? garabik-news-2005-05@kassiopeia.juls.savba.sk - 2015-05-26 20:26 +0000
    Re: should "self" be changed? Mark Lawrence <breamoreboy@yahoo.co.uk> - 2015-05-26 21:45 +0100
      Re: should "self" be changed? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2015-05-27 15:23 +1000
        Re: should "self" be changed? Chris Angelico <rosuav@gmail.com> - 2015-05-27 16:32 +1000
          Re: should "self" be changed? Marko Rauhamaa <marko@pacujo.net> - 2015-05-27 10:39 +0300
            Re: should "self" be changed? Chris Angelico <rosuav@gmail.com> - 2015-05-27 18:20 +1000
            Re: should "self" be changed? zipher <dreamingforward@gmail.com> - 2015-05-30 16:15 -0700
        Re: should "self" be changed? Terry Reedy <tjreedy@udel.edu> - 2015-05-27 17:59 -0400
    Re: should "self" be changed? Vito De Tullio <vito.detullio@gmail.com> - 2015-05-26 23:17 +0200
    Re: should "self" be changed? Tim Chase <python.list@tim.thechases.com> - 2015-05-26 16:10 -0500
  Re: should "self" be changed? Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2015-05-27 15:17 +1000

csiph-web