File: setup.py

package info (click to toggle)
python-clips 1.0.7.348%2Bclips-4
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 2,376 kB
  • sloc: ansic: 17,065; python: 5,668; sh: 20; makefile: 12
file content (941 lines) | stat: -rwxr-xr-x 30,962 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
#!/usr/bin/env python
# setup.py
# setup program for clips module

# (c) 2002-2008 Francesco Garosi/JKS
#  The author's copyright is expressed through the following notice, thus
#  giving effective rights to copy and use this software to anyone, as shown
#  in the license text.
#
# NOTICE:
#  This software is released under the terms of the GNU Lesser General Public
#  license; a copy of the text has been released with this package (see file
#  _license.py, where the license text also appears), and can be found on the
#  GNU web site, at the following address:
#
#           http://www.gnu.org/copyleft/lesser.html
#
#  Please refer to the license text for any license information. This notice
#  has to be considered part of the license, and should be kept on every copy
#  integral or modified, of the source files. The removal of the reference to
#  the license will be considered an infringement of the license itself.


"""PyCLIPS
A Python module to interface the CLIPS expert system shell library."""


__revision__ = "$Id: setup.py 342 2008-02-22 01:17:23Z Franz $"
print "Module 'clips': Python to CLIPS interface"
print "Setup revision: %s" % __revision__


# the following values generate the version number and some of them
#  are modified via an automatic process; version information has been
#  made this detailed in order to help setuptools to better decide
#  whether or not to replace an existing package
PYCLIPS_MAJOR = 1
PYCLIPS_MINOR = 0
PYCLIPS_PATCHLEVEL = 7
PYCLIPS_INCREMENTAL = 348
PYCLIPS_VERSION = "%s.%s.%s.%s" % (
    PYCLIPS_MAJOR,
    PYCLIPS_MINOR,
    PYCLIPS_PATCHLEVEL,
    PYCLIPS_INCREMENTAL)


from glob import glob
import os, sys, re, tempfile


# remove unwanted patch sets from this list (not recommended)
APPLY_PATCHSETS = [
    'ia64',     # ia_64 related fixes
    'bgfx',     # official patches to the CLIPS source
    'test',     # "experimental" (unofficial) but useful patches
    ]

# this will be used to download CLIPS source if not found
CLIPS_SRC_URL = "http://pyclips.sourceforge.net/files/CLIPSSrc.zip"

# standard indentation for conversion (4 spaces)
INDENT = " " * 4


# a shortcut, since it appears many times
_p = os.path.join

# find Clips source directory and zip file
zl, zd = os.listdir("."), {}
for x in zl:
    zd[x.lower()] = x
try:
    ClipsSrcZIP = _p('.', zd['clipssrc.zip'])
except:
    ClipsSrcZIP = "CLIPSSrc.zip"    # will obviously produce an error
ClipsLIB_dir = _p('.', 'clipssrc')
ClipsPATCH_dir = _p('.', 'optpatch')


# make a new setup.h based on current platform (note: this file is a copy
#  of the original setup.h file, and is subject to the license terms that
#  can be found in all the original CLIPS source files). This is the only
#  chunk of code which may not be subject to the same license as the others
#  (although all of this code is released under an Open Source license)
setup_h_templ = """\
/*************************************************************/
/* Principal Programmer(s):                                  */
/*      Gary D. Riley                                        */
/*      Brian L. Donnell                                     */
/* Contributing Programmer(s):                               */
/* Revision History:                                         */
/*************************************************************/
#ifndef _H_setup
#define _H_setup

#define GENERIC 0
#define UNIX_V  %s
#define UNIX_7  0
#define MAC_MCW 0
#define IBM_MCW 0
#define IBM_MSC %s
#define IBM_TBC 0
#define IBM_GCC %s

#define IBM_ZTC 0
#define IBM_ICB 0
#define IBM_SC  0
#define MAC_SC6 0
#define MAC_SC7 0
#define MAC_SC8 0
#define MAC_MPW 0
#define VAX_VMS 0
#define MAC_XCD 0

#if IBM_ZTC || IBM_MSC || IBM_TBC || IBM_ICB || IBM_SC || IBM_MCW
#define IBM 1
#else
#define IBM 0
#endif

#if MAC_SC6 || MAC_SC7 || MAC_SC8
#define MAC_SC 1
#else
#define MAC_SC 0
#endif

#if MAC_SC || MAC_MPW || MAC_MCW || MAC_XCD
#define MAC 1
#else
#define MAC 0
#endif


#if CLIPS_MAJOR >= 6

#if CLIPS_MINOR < 23
#error "Cannot build using CLIPS version less than 6.23"
#endif /* CLIPS_MINOR >= 23 */

#define VOID     void
#define VOID_ARG void
#define STD_SIZE size_t

#if CLIPS_MINOR < 24
#define BOOLEAN int
#else
#define intBool int
#endif /* CLIPS_MINOR < 24 */

#define globle

#define ALLOW_ENVIRONMENT_GLOBALS 1
#define BASIC_IO 1
#define BLOAD 0
#define BLOAD_AND_BSAVE 1
#define BLOAD_ONLY 0
#define BLOAD_INSTANCES 1
#define BLOCK_MEMORY 0
#define BSAVE_INSTANCES 1
#define CONFLICT_RESOLUTION_STRATEGIES 1
#define CONSTRUCT_COMPILER 0
#define DEBUGGING_FUNCTIONS 1
#define DEFFACTS_CONSTRUCT 1
#define DEFFUNCTION_CONSTRUCT 1
#define DEFGENERIC_CONSTRUCT 1
#define DEFGLOBAL_CONSTRUCT 1
#define DEFINSTANCES_CONSTRUCT 1
#define DEFMODULE_CONSTRUCT 1
#define DEFRULE_CONSTRUCT 1
#define DEFTEMPLATE_CONSTRUCT 1
#define EMACS_EDITOR 0
#define ENVIRONMENT_API_ONLY 0
#define EX_MATH 1
#define EXT_IO 1
#define FACT_SET_QUERIES 1
#define HELP_FUNCTIONS 0
#define INSTANCE_SET_QUERIES 1
#define MULTIFIELD_FUNCTIONS 1
#define OBJECT_SYSTEM 1
#define PROFILING_FUNCTIONS 1
#define RUN_TIME 0
#define STRING_FUNCTIONS 1
#define TEXTPRO_FUNCTIONS 1
#define WINDOW_INTERFACE 1

#define DEVELOPER 0

#if CLIPS_MINOR < 24
#define AUXILIARY_MESSAGE_HANDLERS 1
#define DYNAMIC_SALIENCE 1
#define IMPERATIVE_MESSAGE_HANDLERS 1
#define IMPERATIVE_METHODS 1
#define INCREMENTAL_RESET 1
#define INSTANCE_PATTERN_MATCHING 1
#define LOGICAL_DEPENDENCIES  1
#define SHORT_LINK_NAMES 0
#endif  /* CLIPS_MINOR < 24 */

#else   /* CLIPS_MAJOR >= 6 */
#error "Cannot build using CLIPS version less than 6.23"
#endif

#include "envrnmnt.h"

#define Bogus(x)
#define PrintCLIPS(x,y) EnvPrintRouter(GetCurrentEnvironment(),x,y)
#define GetcCLIPS(x,y) EnvGetcRouter(GetCurrentEnvironment(),x)
#define UngetcCLIPS(x,y) EnvUngetcRouter(GetCurrentEnvironment(),x,y)
#define ExitCLIPS(x) EnvExitRouter(GetCurrentEnvironment(),x)
#define CLIPSSystemError(x,y) SystemError(x,y)
#define CLIPSFunctionCall(x,y,z) FunctionCall(x,y,z)
#define InitializeCLIPS() InitializeEnvironment()
#define WCLIPS WPROMPT
#define CLIPSTrueSymbol SymbolData(GetCurrentEnvironment())->TrueSymbol
#define CLIPSFalseSymbol SymbolData(GetCurrentEnvironment())->FalseSymbol
#define EnvCLIPSTrueSymbol(theEnv) SymbolData(theEnv)->TrueSymbol
#define EnvCLIPSFalseSymbol(theEnv) SymbolData(theEnv)->FalseSymbol
#define CLIPS_FALSE 0
#define CLIPS_TRUE 1
#if BLOCK_MEMORY
#define INITBLOCKSIZE 32000
#define BLOCKSIZE 32000
#endif

#include "usrsetup.h"

#endif  /* _H_setup */
"""



# find out symbols that are imported from clipsmodule.c: if we keep
#  using this "protocol" for declaring symbols, it could be used also
#  to read other possible C source files; we define IMPORTED_SYMBOLS
#  here just because it is used as global in some functions below
IMPORTED_SYMBOLS = []
def find_imported_symbols(filename):
    f = open(filename)
    li = [x for x in map(str.strip, f.readlines())
          if x.startswith('ADD_MANIFEST_CONSTANT(')
          or x.startswith('MMAP_ENTRY(')]
    f.close()
    li1 = []
    for x in li:
        if x.startswith('ADD_MANIFEST_CONSTANT('):
            x = x.replace('ADD_MANIFEST_CONSTANT(d,', '')
            x = x.replace(');', '').strip()
        else:
            x = x.replace('MMAP_ENTRY(', '')
            x = x.split(',')[0].strip()
        li1.append(x)
    return li1




# parts of the companion module file
MODULE_TEMPLATE = '''\
# _eclips_wrap.py
# environment aware functions for CLIPS, embedded in an Environment class

# (c) 2002-2008 Francesco Garosi/JKS
#  The Author's copyright is expressed through the following notice, thus
#  giving actual rights to copy and use this software to anyone, as expressed
#  in the license text.
#
# NOTICE:
# This software is released under the terms of the GNU Lesser General Public
#  license; a copy of the text has been released with this package (see file
#  license.py), and can be found on the GNU web site, at the following
#  address:
#
#           http://www.gnu.org/copyleft/lesser.html
#
#  Please refer to the license text for any license information. This notice
#  has to be considered part of the license, and should be kept on every copy
#  integral or modified, of the source files. The removal of the reference to
#  the license will be considered an infringement of the license itself.

"""\
clips - high-level interface to the CLIPS engine module
        (c) 2002-2008 Francesco Garosi/JKS
"""

# standard imports
import sys as _sys

import os as  _os
import types as _types

# the low-level module
import _clips as _c


# ========================================================================== #
# globals

# bring the CLIPS Exception object at top level
ClipsError = _c.ClipsError


# redeclare manifest constants here in order to avoid having to
#  reference the ones defined in te low-level module _clips

# check Python version, and issue an exception if not supported
if _sys.version[:3] < "2.4":
    raise _c.ClipsError("M99: Python 2.4 or higher required")


# these globals are redefined instead of reimported for sake of speed
LOCAL_SAVE = _c.LOCAL_SAVE
VISIBLE_SAVE = _c.VISIBLE_SAVE

WHEN_DEFINED = _c.WHEN_DEFINED
WHEN_ACTIVATED = _c.WHEN_ACTIVATED
EVERY_CYCLE = _c.EVERY_CYCLE

NO_DEFAULT = _c.NO_DEFAULT
STATIC_DEFAULT = _c.STATIC_DEFAULT
DYNAMIC_DEFAULT = _c.DYNAMIC_DEFAULT

DEPTH_STRATEGY = _c.DEPTH_STRATEGY
BREADTH_STRATEGY = _c.BREADTH_STRATEGY
LEX_STRATEGY = _c.LEX_STRATEGY
MEA_STRATEGY = _c.MEA_STRATEGY
COMPLEXITY_STRATEGY = _c.COMPLEXITY_STRATEGY
SIMPLICITY_STRATEGY = _c.SIMPLICITY_STRATEGY
RANDOM_STRATEGY = _c.RANDOM_STRATEGY

CONVENIENCE_MODE = _c.CONVENIENCE_MODE
CONSERVATION_MODE = _c.CONSERVATION_MODE


# import adequate symbols from _clips_wrap
from _clips_wrap import Nil, Integer, Float, String, Symbol, InstanceName, \\
                        Multifield, _cl2py, _py2cl, _py2clsyntax, \\
                        ClipsIntegerType, ClipsFloatType, ClipsStringType, \\
                        ClipsSymbolType, ClipsInstanceNameType, \\
                        ClipsMultifieldType, ClipsNilType, \\
                        _setStockClasses, _accepts_method, _forces_method, \\
                        AROUND, BEFORE, PRIMARY, AFTER



# environment class:
class Environment(object):
    """class representing an environment: implements all global classes"""

%(MEMBER_CLASSES)s

    # constructor possibly sets the "borrowed" flag, to state that this
    #  is a Python class around an existing object: in this case the
    #  underlying CLIPS environment is not attempted to be destroyed on
    #  deletion
    def __init__(self, o=None):
        """environment constructor"""
        if o is None:
            self.__env = _c.createEnvironment()
            self.__borrowed = False
        else:
            if _c.isEnvironment(o):
                self.__env = o
                self.__borrowed = True
            else:
                raise TypeError("invalid argument for constructor")
%(CLASS_INIT)s
        # if o is not None, then this is an internal object and its status
        #  should not be modified by the user, nor the stock objects be
        #  accessible for direct inspection or subclassing (as this could
        #  be the current environment and might be corrupted)
        if o is None:
            self.EngineConfig = self._clips_Status()
            self.DebugConfig = self._clips_Debug()

%(MEMBER_FUNCTIONS)s

    def __del__(self):
        """environment destructor"""
        if not self.__borrowed:
            try:
                _c.destroyEnvironment(self.__env)
            except ClipsError:
                pass

    def __repr__(self):
        """representation of environment, borrowed by underlying object"""
        return "<Environment: " + repr(self.__env)[1:-1] + ">"

    def __property_getIndex(self):
        return _c.getEnvironmentIndex(self.__env)
    Index = property(__property_getIndex, None, None,
                     "Return index of this Environment")

    def SetCurrent(self):
        """Make this Environment the current Environment"""
        _c.setCurrentEnvironment(self.__env)
        _setStockClasses()



# A function that returns current Environment
def CurrentEnvironment():
    """Return current Environment"""
    cenv = _c.getCurrentEnvironment()
    env = Environment(cenv)
    env.EngineConfig = env._clips_Status()
    env.DebugConfig = env._clips_Debug()
    return env



# end.
'''


# this is what all the functions in the low-level module look like
func_re = re.compile(r"(_c\.\w+\()")
func_re_NA = re.compile(r"(_c\.\w+\(\))")
class_re = re.compile(r"^class\ ")
def_re = re.compile(r"def\ (\w+\()")
def_re_NA = re.compile(r"def\ (\w+\(\))")


# ========================================================================== #

ALL_CLASSES = {}
ALL_FUNCTIONS = {}

def useful_line(s):
    s1 = s.strip()
    return bool(s1 and not s1.startswith('#'))

def remove_leading_underscore(s):
    while s[0] == "_":
        s = s[1:]
    return s

# read the module (which contains the special class/function markers) and
#  extract all top-level functions and classes to be put in Environment
def _i_read_module(f):
    global ALL_CLASSES, ALL_FUNCTIONS
    li = f.readlines()
    for i in range(len(li)):
        l = li[i]
        if l.strip() == "#{{CLASS":
            while not li[i].strip().startswith('class'):
                i += 1
            classname = li[i].split('(', 1)[0].replace('class', '').strip()
            text = [li[i]]
            i += 1
            while li[i].strip() != "#}}":
                if useful_line(li[i]):
                    text.append(li[i])
                i += 1
            ALL_CLASSES[classname] = text
        elif l.strip() == "#{{FUNCTION":
            text = []
            while not li[i].strip().startswith('def'):
                if li[i].strip().startswith('@'):
                    text.append(li[i])
                i += 1
            funcname = li[i].split('(', 1)[0].replace('def', '').strip()
            text.append(li[i])
            i += 1
            while li[i].strip() != "#}}":
                if useful_line(li[i]):
                    text.append(li[i])
                i += 1
            ALL_FUNCTIONS[funcname] = text
        i += 1

# hack to convert direct instantiations of classes in return values
def _i_convert_classinstantiation_c(s):
    if s.split()[0] == 'class':
        return s
    for x in ALL_CLASSES.keys():
        s = s.replace(" %s(" % x, " self.__envobject.%s(" % x)
        s = s.replace("(%s(" % x, "(self.__envobject.%s(" % x)
    return s

def _i_convert_classinstantiation(s):
    if s.split()[0] == 'class':
        return s
    for x in ALL_CLASSES.keys():
        s = s.replace(" %s(" % x, " self.%s(" % x)
        s = s.replace("(%s(" % x, "(self.%s(" % x)
    return s


# convert a single line of code and return it indented
def _i_convert_line(s, cvtdef=False):
    if s.strip() == "":
        return ""
    if s.strip()[0] == "#":
        return ""
    s1 = s
    if not s1.strip().startswith("def "):
        s1 = s1.replace(" _cl2py(", " self._cl2py(")
        s1 = s1.replace(" _py2cl(", " self._py2cl(")
        s1 = s1.replace("(_cl2py(", "(self._cl2py(")
        s1 = s1.replace("(_py2cl(", "(self._py2cl(")
    if s1.strip().startswith("@"):
        s1 = s1.replace("@_accepts(", "@_accepts_method(")
        s1 = s1.replace("@_forces(", "@_forces_method(")
    mo = func_re.search(s1)
    if mo:
        li = mo.groups()
        for x in li:
            t = x[3:-1]
            if "env_%s" % t in IMPORTED_SYMBOLS:
                if func_re_NA.search(s1):
                    s1 = s1.replace(x, "_c.env_%s(self.__env" % t)
                else:
                    s1 = s1.replace(x, "_c.env_%s(self.__env, " % t)
    if cvtdef:
        mo = def_re.search(s1)
        if mo:
            if def_re_NA.search(s1):
                s1 = def_re.sub(mo.group(0) + "self", s1)
            else:
                s1 = def_re.sub(mo.group(0) + "self, ", s1)
        for x in ALL_CLASSES.keys():
            s1 = s1.replace(" %s(" % x, " self.%s(" % x)
            s1 = s1.replace(" %s:" % x, " self.%s:" % x)
            s1 = s1.replace("(%s(" % x, "(self.%s(" % x)
    return INDENT + _i_convert_classinstantiation(s1)

def _i_convert_classline(s):
    if s.strip() == "":
        return ""
    if s.strip()[0] == "#":
        return ""
    s1 = s
    if not s1.strip().startswith("def "):
        s1 = s1.replace(" _cl2py(", " self.__envobject._cl2py(")
        s1 = s1.replace(" _py2cl(", " self.__envobject._py2cl(")
        s1 = s1.replace("(_cl2py(", "(self.__envobject._cl2py(")
        s1 = s1.replace("(_py2cl(", "(self.__envobject._py2cl(")
    if s1.strip().startswith("@"):
        s1 = s1.replace("@_accepts(", "@_accepts_method(")
        s1 = s1.replace("@_forces(", "@_forces_method(")
    mo = func_re.search(s1)
    if mo:
        li = mo.groups()
        for x in li:
            t = x[3:-1]
            if "env_%s" % t in IMPORTED_SYMBOLS:
                if func_re_NA.search(s1):
                    s1 = s1.replace(x, "_c.env_%s(self.__env" % t)
                else:
                    s1 = s1.replace(x, "_c.env_%s(self.__env, " % t)
    return INDENT + INDENT + _i_convert_classinstantiation_c(s1)

# convert an entire class, provided as a list of lines
def _i_convert_fullclass(name, li):
    li1 = li[1:]
    docs = []
    while li1[0].strip()[0] in ['"', "'"]:
        docs.append(li1[0])
        li1 = li1[1:]
    head1 = INDENT + "def %s(self, private_environment):\n" % name
    head2 = INDENT + INDENT + "environment_object = self\n"
    head3 = INDENT + INDENT + "class %s(object):\n" % name
    head4 = INDENT + INDENT + INDENT + "__env = private_environment\n"
    head5 = INDENT + INDENT + INDENT + "__envobject = environment_object\n"
    foot1 = INDENT + INDENT + "return %s\n"  % name
    return [head1, head2, head3] + map(_i_convert_classline, docs) \
       + [head4, head5] + map(_i_convert_classline, li1) + [foot1]

# convert an entire function, provided as a list of lines
def _i_convert_fullfunction(name, li):
    return map(lambda x: _i_convert_line(x, True), li)

# create the list of lines that build inner classes in Environment.__init__
def _i_create_inner_classes():
    inner = []
    kclasses = ALL_CLASSES.keys()
    kclasses.sort()
    for x in kclasses:
        inner.append(
            INDENT + INDENT + "self.%s = self.%s(self.__env)\n" % (x, x))
    return inner


# macro to convert all the read module
def convert_module(filename):
    f = open(filename)
    _i_read_module(f)
    f.close()
    classes = []
    kclasses = ALL_CLASSES.keys()
    kclasses.sort()
    for x in kclasses:
        classes += _i_convert_fullclass(x, ALL_CLASSES[x])
    initclasses = _i_create_inner_classes()
    functions = []
    kfunctions = ALL_FUNCTIONS.keys()
    kfunctions.sort()
    for x in kfunctions:
        functions += _i_convert_fullfunction(x, ALL_FUNCTIONS[x])
    return MODULE_TEMPLATE % {
        'MEMBER_CLASSES': "".join(classes),
        'CLASS_INIT': "".join(initclasses),
        'MEMBER_FUNCTIONS': "".join(functions),
        }

# ========================================================================== #


# This retrieves the CLIPS version looking up headers
def get_clips_version(fn):
    li = open(fn).readlines()
    cre = re.compile(r"\#define\ VERSION_STRING\ \"([0-9]+\.[0-9]+)\"")
    vno = "unknown"
    for s in li:
        f = cre.search(s)
        if f:
            vno = f.groups(1)
            break
    return "%s" % vno



# Actual distutils setup routine

# check for CLIPS library files, and eventually uncompress them from
#  the CLIPS source - to be found at CLIPS website. In case no ZIP
#  file is even present here, notice the user that it has to be
#  downloaded and put here, in the directory where this file resides
nozip_notice = """\
ERROR: setup.py could not find the CLIPS source files. These files
       are necessary to build the CLIPS interface module: please
       download the archive (%s) and copy it to the directory
       where setup.py resides. The setup program will take care of
       unpacking the necessary source files in an appropriate place.
"""

badzip_notice = """\
ERROR: setup.py could not read one or more file(s) from the source
       archive. Please provide a good copy of the archive (%s).
"""


# this is used to normalize end-of-line characters across systems, since we
#  use the ZIP version of the archive (which is for Win32/DOS systems). The
#  use of TemporaryFile is a hack, but so we can read lines letting Python
#  do the dirty job of removing bad EOLs.
def normalize_eols(t):
    def _remove_badchars(x):
        t = ""
        x = x.replace('\t', " " * 8)
        for c in x:
            if ord(c) >= 32 and ord(c) < 128:
                t += c
        return t
    tf = tempfile.TemporaryFile()
    tf.write(t)
    tf.seek(0)
    li = tf.readlines()
    tf.close()
    li = map(lambda x: x.rstrip(), li)
    li = map(_remove_badchars, li)
    return li


if not os.path.exists(ClipsLIB_dir):
    if not os.path.exists(ClipsSrcZIP):
        # try to download file from official site
        import urllib
        print "CLIPS source archive (%s) not found, " \
              "trying to download it for you..." % ClipsSrcZIP
        try:
            f = urllib.urlopen(CLIPS_SRC_URL)
            s = f.read()
            if not s:
                raise   # anyway we'll answer that the source wasn't found
            f = open(ClipsSrcZIP, 'wb')
            f.write(s)
            f.close()
            print "Download successful, continuing build."
            print "Please review CLIPS license in the downloaded ZIP file!"
        except:
            print "Download FAILED!"
            print nozip_notice % ClipsSrcZIP
            sys.exit(2)
    import zipfile
    try:
        print "Opening CLIPS source archive (%s)..." % ClipsSrcZIP
        zf = zipfile.ZipFile(ClipsSrcZIP)
        os.mkdir(ClipsLIB_dir)
        li = zf.namelist()
        for x in li:
            n = _p(ClipsLIB_dir, os.path.basename(x))
            if n.endswith('.h') or n.endswith('.c'):
                sys.stdout.write("\tExtracting %s... " % n)
                li = normalize_eols(zf.read(x))
                f = open(n, 'w')
                for t in li:
                    f.write("%s\n" % t)
                f.close()
                sys.stdout.write("done.\n")
        zf.close()
        print "All CLIPS source files extracted, continuing build."
    except zipfile.error:
        print badzip_notice % ClipsSrcZIP
        sys.exit(2)
    except:
        print nozip_notice % ClipsSrcZIP
        sys.exit(2)



# ----------------------------------------------------------------------------
# -- 8-< -- CUT FROM HERE --------------------------------------------- >-8 --

bad_platform = """\
setup.py: setup provided only for posix/win32 systems
NOTE: If you want to provide your own setup.h file, please remove the lines
      between the CUT ... HERE markers and edit your setup.h file using the
      original file (setup.h or setup.h.m_ORIG). Please be cautious doing so,
      since the module expects all the constructs and functions defined in
      the ad-hoc configuration file to be available: modifying setup.h by
      yourself may lead to an inconsistent module or compilation errors.
"""
if sys.platform == "win32":
    defines = (0, 1, 0)
elif os.name == "posix":
    defines = (0, 0, 1)
else:
    sys.stderr.write(bad_platform)
    sys.exit(2)

setup_h = setup_h_templ % defines

# rename setup.h to setup.h.ORIG to keep a copy of original file; if the
#  copy already exists, we are supposed to already have built the new
#  CLIPS setup file, thus we do not
setup_orig_name = _p(ClipsLIB_dir, 'setup.h')
setup_copy_name = _p(ClipsLIB_dir, 'setup.h.m_ORIG')
if os.path.exists(setup_orig_name) and not os.path.exists(setup_copy_name):
    sys.stdout.write("copying setup.h to setup.h.m_ORIG, making new header...")
    os.rename(setup_orig_name, setup_copy_name)
    f = open(setup_orig_name, 'w')
    f.write(setup_h)
    f.close()
    sys.stdout.write("Done.\n")

# -- 8-< -- CUT UP TO HERE -------------------------------------------- >-8 --
# ----------------------------------------------------------------------------


# here we remove the (exit) function from CLIPS to prevent aborting Python
routerc_orig_name = _p(ClipsLIB_dir, 'router.c')
routerc_copy_name = _p(ClipsLIB_dir, 'router.c.m_ORIG')
if os.path.exists(routerc_orig_name) and not os.path.exists(routerc_copy_name):
    sys.stdout.write("copying router.c to router.c.m_ORIG, making new source...")
    f = open(routerc_orig_name)
    li = f.readlines()
    f.close()
    os.rename(routerc_orig_name, routerc_copy_name)
    badline_re = re.compile(
        "\s*EnvDefineFunction2.+exit.+ExitCommand.+ExitCommand.+")
    f = open(routerc_orig_name, 'w')
    for l in li:
        if badline_re.match(l):
            f.write("/* INTENTIONALLY SKIPPED */\n")
            f.write("#ifndef PYCLIPS\n")
            f.write(l)
            f.write("#endif /* PYCLIPS */\n")
        else:
            f.write(l)
    f.close()
    sys.stdout.write("Done.\n")


# remove some source files as they interfere with clipsmodule.c
TO_REMOVE = [
    'main.c',
    'userfunctions.c',
    ]

all_clipssrc = sorted(glob(_p(ClipsLIB_dir, '*.c')))
main_clipssrc = ['clipsmodule.c', 'clips_or.c']
for x in all_clipssrc:
    if os.path.basename(x) in TO_REMOVE:
        all_clipssrc.remove(x)


# actually build "companion" module: first we calculate the list of
#  all symbols that are imported from the low-level module, and then
#  we read the environment-unaware high-level module to build the
#  environment-aware part using the above helpers
sys.stdout.write("finding low-level module symbols... ")
IMPORTED_SYMBOLS = find_imported_symbols(_p('.', 'clipsmodule.c'))
sys.stdout.write("Done!\nbuilding environment-aware submodule... ")
s = convert_module(_p('clips', '_clips_wrap.py'))
f = open(_p('clips', '_eclips_wrap.py'), 'w')
f.write(s)
f.close()
sys.stdout.write("Done!\n")


# retrieve used CLIPS version
clips_version = get_clips_version(_p("clipssrc", "constant.h"))
print "Found CLIPS version: %s" % clips_version
maj, min = clips_version.split('.', 1)
CFLAGS = [
    '-DPYCLIPS',
    '-DCLIPS_MAJOR=%s' % maj,
    '-DCLIPS_MINOR=%s' % min,
    '-DPYCLIPS_MAJOR=%s' % PYCLIPS_MAJOR,
    '-DPYCLIPS_MINOR=%s' % PYCLIPS_MINOR,
    '-DPYCLIPS_PATCHLEVEL=%s' % PYCLIPS_PATCHLEVEL,
    '-DPYCLIPS_INCREMENTAL=%s' % PYCLIPS_INCREMENTAL,
    ]


# if we are using GCC we must set an extra option; for now we assume that
#  GCC is used unless, on Win32, compiler is explicitly specified as one
#  of 'unix', 'mingw32', 'cygwin'; note that this will force to *always*
#  specify compiler even when using setuptools and the default compiler
#  is set to be one of the above; maybe one day I will discover a better
#  way to determine which compiler is used without having to rewrite all
#  of distutils
if sys.platform == 'win32':
    try:
        find_compiler = sys.argv.index('-c') + 1
    except ValueError:
        find_compiler = 0
    uses_gcc = (
        (find_compiler and
            find_compiler < len(sys.argv) and
            sys.argv[find_compiler] in (
                'unix', 'mingw32', 'cygwin')) or
        ('--compiler=unix' in sys.argv) or
        ('--compiler=mingw32' in sys.argv) or
        ('--compiler=cygwin' in sys.argv))
else:
    uses_gcc = True
if uses_gcc:
    CFLAGS.append('-fno-strict-aliasing')


# apply "optional" patches
i, o, e = os.popen3("patch --version")
vs = o.read()
o.close()
e.close()
if vs:
    print "'patch' utility found, applying selected patchsets..."
    import shutil
    def apply_patchset(ps):
        print "Applying patchset '%s':" % ps
        pattern = "*.[ch]-??.v%s-%s.diff" % (clips_version, ps)
        for x in glob(_p(ClipsPATCH_dir, pattern)):
            pfn = os.path.basename(x)
            sourcefile = pfn.split('-', 1)[0]
            if not os.path.exists(_p(ClipsLIB_dir, "%s.ORIG" % sourcefile)):
                sys.stdout.write(
                    "patching %s (original in %s.ORIG)... " % (
                        sourcefile, sourcefile))
                shutil.copy(
                    _p(ClipsLIB_dir, sourcefile),
                    _p(ClipsLIB_dir, "%s.ORIG" % sourcefile)
                    )
                patchcmd = "patch -l -s -p0 %s < %s" % (
                    _p(ClipsLIB_dir, sourcefile), x)
                if not os.system(patchcmd):
                    print "ok."
                else:
                    print "FAILED"
    for x in APPLY_PATCHSETS:
        apply_patchset(x)


# create the version submodule
sys.stdout.write("Creating version number: ")
f = open(_p('clips', '_version.py'), 'w')
f.write("""# version number
version_string = "%s"
version = (%s, %s, %s, %s)
""" % (PYCLIPS_VERSION, PYCLIPS_MAJOR,
       PYCLIPS_MINOR, PYCLIPS_PATCHLEVEL,
       PYCLIPS_INCREMENTAL))
f.close()

# start setup
print "Standard setup in progress:"


# The following is a warning to users of ez_setup when using GCC (for
# instance MinGW) and it is set as the default compiler, since it is
# difficult for me to detect which compiler is used.
warn_default_gcc = """
WARNING: if you are using setuptools and GCC (eg. MinGW) as your default
         compiler, it has not been detected. This can lead to unexpected
         behaviours such as crashes. If you experience such behaviours,
         please rebuild the module specifying, for instance, the option
         '--compiler=mingw32' on the setup command line.
"""

# possibly use ez_setup/setuptools, or standard distutils if not possible
# and if not using a debug executable (this is mainly because it would
# pollute MY setup): the following way to discover under what circumstances
# we are running is definitely hideous
DEBUGGING = bool(
    sys.executable.endswith('_d')
    or sys.executable.endswith('_d.exe'))
if not DEBUGGING:
    try:
        import ez_setup
        ez_setup.use_setuptools()
        from setuptools import setup, Extension
        print "Using setuptools instead of distutils..."
        if not uses_gcc:
            print warn_default_gcc
    except:
        from distutils.core import setup, Extension
else:
    from distutils.core import setup, Extension


setup(name="pyclips",
      version="%s-clips_%s" % (PYCLIPS_VERSION, clips_version),
      description="Python CLIPS interface",
      long_description=__doc__,
      author="Francesco Garosi",
      author_email="franzg@users.sourceforge.net",
      url="http://pyclips.sourceforge.net",
      packages=['clips'],
      ext_modules=[
          Extension('clips._clips',
                    main_clipssrc + all_clipssrc,
                    extra_compile_args=CFLAGS,
                    include_dirs=[ClipsLIB_dir]),
          ],
     )


# end.