{{main_text{本文为蒋洪波(LarkDream)原创,版权归蒋洪波(LarkDream)所有,欢迎各网站转载。转载时请保持原文完整并保留版权信息。\n----\n 访问blog: [[lark.blogchina.com|http:\s\slark.blogchina.com]]以获取最新版本。
To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:\n* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)\n* MainMenu: The menu (usually on the left)\n* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened\nYou'll also need to enter your username for signing your edits: <<option txtUserName>>
[[序言]]\n[[模块]]\n[[类]]\n[[异常]]\n[[网络]]\n[[单元测试]]\n[[XRC初步]]\n[[python风格导引]]\n[[wxPython风格导引]]\n[[CopyRight]]
LarkDream's python点滴.
.main_text\n{\nfont-size: 10pt\n}
{{main_text{wxPython中定义用户界面布局可以采用两种方法。传统的方法是将界面布局代码直接写在源文件之中,这种方法不需要学习XML语法,概念上较为简单,避免了同时维护多个文件的麻烦;同时,对IDE的自动完成的支持较好。另一种方法则是采用XML资源文件,即XRC(XML Resource)。XRC的基本出发点是界面布局和程序逻辑的分离,即将界面布局代码采用XML的方式放在分离的文件之中;在程序中不涉及控件的创建和布局,只需要加载相应的资源并处理事件绑定即可。在wxPython中采用XRC分离界面布局和程序逻辑主要有以下几个方面的好处:}}}\n## 提高了程序的可维护性\n## 界面布局可由专业人员设计\n## 更好的支持GUI布局设计工具\n## 作为wxWidgets的规范,XRC资源可通用于C++、Python等语言\n!!! 示例\n {{main_text{使用XRC建立用户界面必须建立两个分离的文件:XML资源文件(.xrc)和python源文件。XRC文件包含了用户界面布局信息,如下例:}}}\n{{{\n#design layout in a separate XML file\n\n<?xml version="1.0" encoding="utf-8"?>\n<resource>\n <object class="wxFrame" name="mainFrame">\n <title>My Frame</title>\n <object class="wxPanel" name="panel">\n <object class="wxFlexGridSizer">\n <cols>2</cols>\n <rows>3</rows>\n <vgap>5</vgap>\n <hgap>5</hgap>\n <object class="sizeritem">\n <object class="wxStaticText" name="label1">\n <label>First name:</label>\n </object>\n </object>\n <object class="sizeritem">\n <object class="wxTextCtrl" name="text1"/>\n </object>\n <object class="sizeritem">\n <object class="wxStaticText" name="label2">\n <label>Last name:</label>\n </object>\n </object>\n <object class="sizeritem">\n <object class="wxTextCtrl" name="text2"/>\n </object>\n <object class="spacer">\n <size>0,0</size>\n </object>\n <object class="sizeritem">\n <object class="wxButton" name="button">\n <label>Submit</label>\n </object>\n </object>\n </object>\n </object>\n </object>\n</resource>\n}}}\n {{main_text{Python源文件中则包括加载资源文件和事件绑定代码,同时可以使用xrc.XRCCTRL方法从名字获取控件,使用xrc.XRCID方法获取控件ID:}}}\n{{{\n#logic by itself in module\n\nimport wx\nfrom wx import xrc\n\n\nclass MyApp(wx.App):\n\n def OnInit(self):\n self.res = xrc.XmlResource('gui.xrc')\n self.init_frame()\n return True\n\n def init_frame(self):\n self.frame = self.res.LoadFrame(None, 'mainFrame')\n self.panel = xrc.XRCCTRL(self.frame, 'panel')\n self.text1 = xrc.XRCCTRL(self.panel, 'text1')\n self.text2 = xrc.XRCCTRL(self.panel, 'text2')\n self.frame.Bind(wx.EVT_BUTTON, self.OnSubmit, id=xrc.XRCID('button'))\n self.frame.Show()\n\n def OnSubmit(self, evt):\n wx.MessageBox('Your name is %s %s!' %\n (self.text1.GetValue(), self.text2.GetValue()), 'Feedback')\n\n\nif __name__ == '__main__':\n app = MyApp(False)\n app.MainLoop()\n}}}\n!!!创建XRC文件\n {{main_text{首先我们看看XRC的组成。XRC文件由一系列预定义的标签组成,你可以注意到这些预定义的标签与控件创建时的关键字参数相似。例如,在wxPython中创建按键的代码如下:}}}\n{{{\nbutton = wx.Button(parent = panel, id = wx.ID_ANY, label = 'Submit')\n}}}\n {{main_text{与之相应的XRC代码则是这个样子:}}}\n{{{\n<object class="wxButton" name="button">\n <label>Submit</label>\n</object>\n}}}\n {{main_text{从例示中可以注意到几个XRC的关键概念:}}}\n## 每个控件与一个XML节点相对应\n## 控件间的层次关系与XML节点间层次关系相对应,放置控件的容器与相应的XML节点的父节点相对应\n## 节点class属性值对应控件的C++类名\n## 节点name属性值为控件在XRC文件中的唯一标识,可通过该值自python源文件中获取控件\n## label等其余参数在XRC文件中作为节点的子节点出现\n {{main_text{有了这些基本概念,现在我们可以使用XRC文件来布局我们的用户界面了。存大大量的工具以可视化的方式来帮助我们完成这个繁琐的工作。wxPython自带的XRCED以一种半可视化的方式(可视化的设定控件属性并预览,但不支持直接拖放控件)支持用户界面设计;开源的wxglade则支持直接的控件拖放,并可生成C++、python、perl、lisp和XRC的源文件;wxDesigner、dialogBlock,wxDesigner,BOA等工具同样值得一试。}}}\n!!!python源文件中的处理\n {{main_text{完成了XRC界面布局文件的编写,下一步必须在python源文件中做相应的处理。与XRC处理相关的wxPython代码段主要包括三个方面:资源文件的加载,控件的获取和事件绑定代码:}}}\n\n {{main_text{1.导入与XRC处理相关的xrc模块:}}}\n{{{\nimport wx.xrc as xrc\n}}}\n {{main_text{2.加载资源文件:}}}\n{{{\nself.res = xrc.XmlResource('XRCfilename.xrc')\n}}}\n {{main_text{3.获取控件。此过程不需要显式的创建控件,通过设计XRC文件时相应的name属性值获取即可:}}}\n{{{\n# 加载Frame,第一个参数为父窗口,当前为顶层窗口,故为None\n# 第二个参数为设计XRC文件时Frame的name属性值\nself.frame = self.res.LoadFrame(None, 'mainFrame')\n\n# 获取控件,第一个参数为控件的父窗口\n# 第二个参数为设计XRC文件时控件的name属性值\nself.panel = xrc.XRCCTRL(self.frame, 'panel')\nself.text1 = xrc.XRCCTRL(self.panel, 'text1')\n\n# 获取panel中的控件时不必显式获取panel本身,直接传入self.frame即可递归向下查找\nself.text2 = xrc.XRCCTRL(self.frame, 'text2')\n\n# 获取控件ID,参数为设计XRC文件时控件的name属性值\nself.id=xrc.XRCID('button')\n}}}\n {{main_text{4.绑定事件处理有两种方式:}}}\n** 绑定按键事件:\n{{{\nself.button = xrc.XRCCTRL(self.panel, 'button')\nself.frame.Bind(wx.EVT_BUTTON, self.OnSubmit, self.button)\n}}}\n** 因为xrc.XRCCTRL仅能返回wx.Window的派生类,而wxMenuItem不是派生自wx.Window,不能采用xrc.XRCCTRL获取菜单项,因此事件必须委托给Frame绑定。派生自wx.Window的按键等控件也可以采用本方法绑定:\n{{{\nself.frame.Bind(wx.EVT_BUTTON, self.OnSubmit, id=xrc.XRCID('button'))\n}}}\n!!!动态创建\n {{main_text{加载Frame窗体时将自动加载其子控件和子窗体,那么,你如何控制子窗体和子控件的加载呢?为此,你必须将子窗体定义为和窗体节体平行的顶级节点(Frames、Dialogs、Panels、Toolbars、Menus和MenuBars可以作为顶级节点),并通过程序控制加载。如下例:}}}\n\ndynamic.xrc文件:\n{{{\n<?xml version="1.0" encoding="utf-8"?>\n<resource>\n <object class="wxFrame" name="mainFrame">\n <title>Primary Frame</title>\n <object class="wxPanel" name="mainPanel">\n <object class="wxButton" name="button">\n <label>New Frame</label>\n </object>\n </object>\n </object>\n <object class="wxFrame" name="nextFrame">\n <title>Secondary Frame</title>\n <object class="wxPanel" name="nextPanel">\n <object class="wxStaticText" name="label">\n <label>This is a dynamically created frame.</label>\n </object>\n </object>\n </object>\n</resource>\n}}}\nPython源文件:\n{{{\nimport wx\nfrom wx import xrc\n\n\nclass MyApp(wx.App):\n\n def OnInit(self):\n self.res = xrc.XmlResource('dynamic.xrc')\n self.init_frame()\n return True\n\n def init_frame(self):\n self.frame = self.res.LoadFrame(None, 'mainFrame')\n self.panel = xrc.XRCCTRL(self.frame, 'mainPanel')\n self.button = xrc.XRCCTRL(self.panel, 'button')\n self.frame.Bind(wx.EVT_BUTTON, self.OnNewFrame, self.button)\n self.frame.Show()\n\n def OnNewFrame(self, evt):\n self.frame2 = self.res.LoadFrame(None, 'nextFrame')\n self.frame2.Show()\n\n\nif __name__ == '__main__':\n app = MyApp(False)\n app.MainLoop()\n}}}\n {{main_text{动态添加子控件时必须首先调用父窗口的GetSizer方法,创建控件后添加到Sizer,并调用Sizer的Fit方法为控件留出显示空间:}}}\n{{{\n MainWindow = res.LoadFrame(None, "frame_1")\n my_sizer = MainWindow.GetSizer()\n my_text = wx.TextCtrl(MainWindow, -1, "Test", size=(325, 200))\n my_sizer.Add(my_text, 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)\n my_sizer.Fit(MainWindow)\n}}}
* Pyhon支持三引号、圆括号、方括号和花括号内的行延续,必要时使用续行符 \s\n\n* 多使用import导入并放在模块注释和文档字符串之后\n\n* docstring为配合pydoc使用,可采用下述约定样式:\n{{{\n#!/usr/bin/python\n\n"""Show off features of [pydoc] module\n\nThis is a silly module to\ndemonstrate docstrings\n"""\n__author__ = 'David Mertz'\n__version__= '1.0'\n__nonsense__ = 'jabberwocky'\n\nclass MyClass:\n """Demonstrate class docstrings"""\n def __init__ (self, spam=1, eggs=2):\n """Set default attribute values only\n\n Keyword arguments:\n spam ― a processed meat product\n eggs ― a fine breakfast for lumberjacks\n """\n self.spam = spam\n self.eggs = eggs\n}}}\npydoc将输出\n{{{\n% pydoc.py mymod\nPython Library Documentation: module mymod\n\nNAME\n mymod - Show off features of [pydoc] module\n\nFILE\n /articles/scratch/cp18/mymod.py\n\nDESCRIPTION\n This is a silly module to\n demonstrate docstrings\n\nCLASSES\n MyClass\n\n class MyClass\n | Demonstrate class docstrings\n |\n | __init__(self, spam=1, eggs=2)\n | Set default attribute values only\n |\n | Keyword arguments:\n | spam ― a processed meat product\n | eggs ― a fine breakfast for lumberjacks\n\nDATA\n __author__ = 'David Mertz'\n __file__ = './mymod.pyc'\n __name__ = 'mymod'\n __nonsense__ = 'jabberwocky'\n __version__ = '1.0'\n\nVERSION\n 1.0\n\nAUTHOR\n David Mertz\n}}}\n* 模块应该是不含下划线的,简短的,小写的名字\n\n* 类名总是使用首字母大写单词串(CapWords)的约定\n\n* 异常通常用"Error" 给尾,并倾向使用CapWords异常名\n\n* 函数、全局变量名应该为小写,可能用下划线风格单词(get_name)以增加可读性\n\n* 方法名、实例变量名应该为小写,可能用下划线风格单词(get_name)以增加可读性\n** 使用一个前导下划线仅用于不打算作为类的公共接口的内部方法和实例变量\n** 使用两个前导下划线以表示类私有的名字 (私有与非公有的区别在于: 前者永远不会被用在一个派生类中)
* 使用import wx,绝对不要使用from wxPython.wx import *。对层次很深的模块使用import wx.lib.somelib as somelib\n\n* 调用构造函数(constructor)时使用命名参数,因为显式胜过隐含:\n{{{\nMainFrame = wx.Frame(None, title="A Title", size=(500, 400)) \n}}}\n* 不要使用IDs,除非你有很充分的理由:\n## 大多数控件的构造函数可以自动为你填充缺省ID\n## 调用必须手动填充时请使用wx.ID_ANY\n## 使用标准ID以使用标准菜单、按键等\n## 必须定义ID时使用wx.NewId(),不要显式指派某个数字为ID值\n\n* 使用Bind()绑定事件\n** 绑定按键事件:\n{{{\nAButton = wx.Button(self, label="Push Me")\nAButton.Bind(wx.EVT_BUTTON, self.OnButtonPush)\n}}}\n** 对没有Bind()方法的菜单可以委托给Frame绑定:\n{{{\nFileMenu = wx.Menu() \nitem = FileMenu.append(wx.ID_ANY, "&Quit") \nself.Bind(wx.EVT_MENU, self.OnQuit, item) #self为wx.Frame\n}}}\n* 使用Sizer进行定位\n\n* 为提高可维护性和重用性,采用分离的定制类代替在某个类中嵌入的大量wx.Panel\n\n* 尽可能使用python提供的方法,而不是wx中的方法。例如采用size=(500, 400)代替size=wx.Size(500, 400)
\n! Header Samples\n!Header 1\n!!Header 2\n!!!Header 3\n!!!!Header 4\n!!!!!Header 5\n\n! Unordered Lists:\n* Lists are where it's at\n* Just use an asterisk and you're set\n** To nest lists just add more asterisks...\n***...like this\n* The circle makes a great bullet because once you've printed a list you can mark off completed items\n* You can also nest mixed list types\n## Like this\n\n! Ordered Lists\n# Ordered lists are pretty neat too\n# If you're handy with HTML and CSS you could customize the [[numbering scheme|http://www.w3schools.com/css/pr_list-style-type.asp]]\n## To nest, just add more octothorpes (pound signs)...\n### Like this\n* You can also\n** Mix list types\n*** like this\n# Pretty neat don't you think?\n\n! Tiddler links\nTo create a Tiddler link, just use mixed-case WikiWord, or use [[brackets]] for NonWikiWordLinks. This is how the GTD style [[@Action]] lists are created. \n\nNote that existing Tiddlers are in bold and empty Tiddlers are in italics. See CreatingTiddlers for details.\n\n! External Links\nYou can link to [[external sites|http://google.com]] with brackets. You can also LinkToFolders on your machine or network shares.\n\n! Images\nEdit this tiddler to see how it's done.\n[img[http://img110.echo.cx/img110/139/gorilla8nw.jpg]]\n\n!Tables\n|!th1111111111|!th2222222222|\n|>| colspan |\n| rowspan |left|\n|~| right|\n|colored| center |\n|caption|c\n\nFor a complex table example, see PeriodicTable.\n\n! Horizontal Rules\nYou can divide a tiddler into\n----\nsections by typing four dashes on a line by themselves.\n\n! Blockquotes\n<<<\nThis is how you do an extended, wrapped blockquote so you don't have to put angle quotes on every line.\n<<<\n>level 1\n>level 1\n>>level 2\n>>level 2\n>>>level 3\n>>>level 3\n>>level 2\n>level 1\n\n! Other Formatting\n''Bold''\n==Strike==\n__Underline__\n//Italic//\nSuperscript: 2^^3^^=8\nSubscript: a~~ij~~ = -a~~ji~~\n@@highlight@@ Unfortunately highlighting is broken right now.\n@@color(green):green colored@@\n@@bgcolor(#ff0000):color(#ffffff):red colored@@ Hex colors are also broken right now.\n\n!预格式化文本\n{{{\nimport exceptions\nprint exceptions.__doc__\n}}}\n----\n! 设置字体字号,使用CSS\n{{main_text{添加StyleSheet,在其中使用:}}}\n{{{\n.classname\n{\nfont-size: 10pt\n}\n}}}\n{{main_text{并在需设置格式处使用:}}}并在需设置格式处使用\n{{{\n{{classname{正文}}}\n}}}
* 测试用例类习惯上使用Test结尾,方法使用test开头,Eclipse的Run as python unit-test可以自动查找并运行符合该规则的测试。\n\n* 测试用例模板\n{{{\nimport unittest\n\nclass simpleTest(unittest.TestCase):\n def setUp(self):\n pass\n\n def tearDown(self):\n pass\n\n def testExample(self):\n self.assertEqual(len('abc'), 3)\n\n def testOther(self):\n self.assertNotEqual('abc', 'ABC')\n self.failUnless(0 == 1)\n\nif __name__ == '__main__':\n unittest.main()\n}}}\n* doctest是python提供的另一个单元测试模块,充分利用了python的特性:在文档字符串(docstrings)内嵌入注释以显示各种语句的期望行为。但个人感觉不如unittest好用,这可能和我习惯使用JUnit和CppUnit有关吧。
{{main_text{Python是一门有趣的程序设计语言。我也算一名老程序员了,虽说一直不太称职,却也尝试过多种不同的语言。历经了C++带给我的征服语法间复杂交互作用的成就感,Delphi的RAD能力带来的界面设计所见即所得的快感,Java中处处体现的框架与设计模式带来的智力上的愉悦感之后,现在,我所喜爱的是python带来的编程的流畅感。\n\n 没错,python正是一门可以让你流畅编程的语言,是美国人所提畅的plain English在程序设计语言的体现。plain English所强调的是用简单的单词和明了的语法来表达复杂的思想,正合于python的设计思想。言以载道,最佳的语言,就是那种在表达中透明存在的语言:无需对语言本身多做考虑,却不会限制你表达的自由。不能说python是合于这个目的,但它却是我所知最接近于这个目的的。\n\n python适用的领域是探索性的。脚本语言是不适合于工业级产品的需要的,但探索性的工作却正是它的所长。动态类型带来的灵活性、语法的plain性、表达力的丰富性让NASA等科研机构纷纷将探索特定领域的开发包构建于python之上。众所周知,受执行效率的限制,脚本语言在科研领域取得成功,这是不多见的。\n\n python的设计哲学是实用主义。是的,python拒绝一切可能限制它的实际使用的条条框框:OO是好的,但OO纯化论者请走开;函数式编程是好的,但不会万事万物皆是函数;泛型是好的,却选择了执行期泛型,远离了生涩难懂的模板机制。事实上,python正可视为溶合了种种有用技术的魔法大溶炉呢。\n\n 先生的拿来主义在python中得到了最淋漓的表现:如果说别的语言为C语言提供的是一个接口,python提供的则是整个语言的一个剖面。由此,C/C++语言下库的丰饶成功的转化为python语言下库的丰饶,脚本语言固有的效率问题也迎刃而解。\n\n 学习python是一段愉快的经历。虽然长期使用静态语言形成的思路还时时束缚着我,未能充分展现python语言的特点,或如行话所言不够pythonic,这里还是记录下我学习中的点点滴滴。一则记录下自己走过的足迹,再则也希望这点点的萤光,能为后来者照亮点点的道路罢。}}}
\n!!! 查看系统异常树\n{{{\nimport exceptions\nprint exceptions.__doc__\n}}}\n\n!!! 新类与异常\npython2.4中异常类为古典类,系统异常自Exception派生;2.5中异常类为新风格类,自BaseException派生。2.4中自定义异常类派生自Exception及其子类,并有描述性文档字符串:\n{{{\nclass MessageError(Exception):\n"""Base class for errors in the email package."""\n}}}\n
\n!!! 模块查找路径\n# 被执行程序所在目录\n# 所设置的PYTHONPATH目录\n# 标准库目录\n# 所设置的.pth文件目录\n\n!!! 查看模块查找路径\nsys.path 中保存着模块查找路径,按自左向右次序查找;可在程序中改变、添加\n\n!!! 模块名串导入\n可采用运行时的串值作为导入模块名,使用如下代码:\n{{{\nmodname = "sys"\nexec "import" + modname\n}}}
\n!!!新风格类\n自object和内建类型派生者为新风格类(New style class),主要有几个特点:\n* 引入了静态方法和类方法,采用以下形式定义:\n{{{\n@staticmethod\ndef ClassName():\n或\n@classmethod\ndef ClassName(cls): #类方法参数习惯命名为cls\n}}}\n* 引入属性(property)的概念,定义形式如:\n{{{\n#原型定义为property(fget=None, fset=None, fdel=None, doc=None)\n\n1 class Rectangle(object):\n2 def __init__(self, width, heigth):\n3 self.width = width\n4 self.heigth = heigth\n5 def getArea(self):\n6 return self.width * self.heigth\n7 area = property(getArea, doc='area of the rectangle')\n}}}\n* 新增名为{{{__new__}}}的静态方法,在类创建前调用,可用作为构造函数\n\n* 新增{{{__slot__}}}属性以节约每个实例中的dictionary占用的空间\n\n* 与{{{__getattr__}}}在查找属性的最后调用不同,新增的{{{__getattribute__}}}方法将在查找属性之初调用\n\n* 隐式调用(重载的操作符等)将调动类中定义方法,即使在实例中重新定义了该方法\n\n* 方法解析改为广度优先搜索\n\n* 采用协作式调用超类方法:\n{{{\nsuper(className,instanceName).methodName( ) \n}}}
\n!!! 发送电子邮件\n使用smtplib模块发送电子邮件的代码:\n{{{\nimport smtplib \nserver = smtplib.SMTP("smtp server address")\nserver.login("username","password") #仅smtp服务器需要验证时\n\n#三引号内为邮件客户端所显示的发送者、接受者地址,可虚构\n#注意正文和邮件头间的空行\n\nserver.sendmail('sender address', ['list of recipients'],\n"""From: sender address \n To: recipients address \n Subject: test smtplib\n\n this is my first e_email by python\n """)\n\nserver.quit()\n}}}\n!!! 发送电子邮件附件\n可采用email模块发送电子邮件附件。发送一个未知MIME类型的文件附件其基本思路如下:\n# 构造MIMEMultipart对象做为根容器\n# 构造MIMEText对象做为邮件显示内容并附加到根容器\n# 构造MIMEBase对象做为文件附件内容并附加到根容器\n## 读入文件内容并格式化\n## 设置附件头\n# 设置根容器属性\n# 得到格式化后的完整文本\n# 用smtp发送邮件\n\n具体内容参见<programing python(3rd)> 14章第6节 "email: Parsing and Composing Mails"。发送一个未知MIME类型的文件附件实例代码如下:\n{{{\nimport smtplib\nimport email.MIMEMultipart\nimport email.MIMEText\nimport email.MIMEBase\nimport os.path\n\nFrom = "sender address"\nTo = "recipients"\nfile_name = "file name"\n\nserver = smtplib.SMTP("smtp server address")\nserver.login("username","password") #仅smtp服务器需要验证时\n\n# 构造MIMEMultipart对象做为根容器\nmain_msg = email.MIMEMultipart.MIMEMultipart()\n\n# 构造MIMEText对象做为邮件显示内容并附加到根容器\ntext_msg = email.MIMEText.MIMEText("this is a test text to text mime")\nmain_msg.attach(text_msg)\n\n# 构造MIMEBase对象做为文件附件内容并附加到根容器\ncontype = 'application/octet-stream'\nmaintype, subtype = contype.split('/', 1)\n\n## 读入文件内容并格式化\ndata = open(file_name, 'rb')\nfile_msg = email.MIMEBase.MIMEBase(maintype, subtype)\nfile_msg.set_payload(data.read( ))\ndata.close( ) \nemail.Encoders.encode_base64(file_msg) \n\n## 设置附件头\nbasename = os.path.basename(file_name)\nfile_msg.add_header('Content-Disposition', \n 'attachment', filename = basename)\nmain_msg.attach(file_msg)\n\n# 设置根容器属性\nmain_msg['From'] = From\nmain_msg['To'] = To \nmain_msg['Subject'] = "attach test " \nmain_msg['Date'] = email.Utils.formatdate( )\n\n# 得到格式化后的完整文本\nfullText = main_msg.as_string( )\n\n# 用smtp发送邮件\ntry:\n server.sendmail(From, To, fullText)\nfinally:\n server.quit()\n}}}