bzr branch
http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
|
1185.12.49
by Aaron Bentley
Switched to ConfigObj |
1 |
=================================== |
2 |
Validation Schema with validate.py |
|
3 |
=================================== |
|
4 |
||
5 |
-------------------------- |
|
6 |
Using the Validator class |
|
7 |
-------------------------- |
|
8 |
||
9 |
||
10 |
:Authors: `Michael Foord`_, `Nicola Larosa`_, `Mark Andrews`_ |
|
11 |
:Version: Validate 0.2.0 |
|
12 |
:Date: 2005/08/25 |
|
13 |
:Homepage: `Validate Homepage`_ |
|
14 |
:License: `BSD License`_ |
|
15 |
:Support: `Mailing List`_ |
|
16 |
||
17 |
.. _Mailing List: http://lists.sourceforge.net/lists/listinfo/configobj-develop |
|
18 |
.. _Michael Foord: fuzzyman@voidspace.org.uk |
|
19 |
.. _Nicola Larosa: nico@teknico.net |
|
20 |
.. _Mark Andrews: mark@la-la.com |
|
21 |
.. _This Document: |
|
22 |
.. _Validate Homepage: http://www.voidspace.org.uk/python/validate.html |
|
23 |
.. _BSD License: http://www.voidspace.org.uk/documents/BSD-LICENSE.txt |
|
24 |
||
25 |
||
26 |
.. contents:: Validate Manual |
|
27 |
||
28 |
Introduction |
|
29 |
============ |
|
30 |
||
31 |
Validation is used to check that supplied values conform to a specification. |
|
32 |
||
33 |
The value can be supplied as a string, e.g. from a config file. In this case |
|
34 |
the check will also *convert* the value to the required type. This allows you |
|
35 |
to add validation as a transparent layer to access data stored as strings. The |
|
36 |
validation checks that the data is correct *and* converts it to the expected |
|
37 |
type. |
|
38 |
||
39 |
Checks are also strings, and are easy to write. One generic system can be used |
|
40 |
to validate information from different sources, via a single consistent |
|
41 |
mechanism. |
|
42 |
||
43 |
Checks look like function calls, and map to function calls. They can include |
|
44 |
parameters and keyword arguments. These arguments are passed to the relevant |
|
45 |
function by the ``Validator`` instance, along with the value being checked. |
|
46 |
||
47 |
The syntax for checks also allows for specifying a default value. This default |
|
48 |
value can be ``None``, no matter what the type of the check. This can be used |
|
49 |
to indicate that a value was missing, and so holds no useful value. |
|
50 |
||
51 |
Functions either return a new value, or raise an exception. See `Validator |
|
52 |
Exceptions`_ for the low down on the exception classes that ``validate.py`` |
|
53 |
defines. |
|
54 |
||
55 |
Some standard functions are provided, for basic data types; these come built |
|
56 |
into every validator. Additional checks are easy to write: they can be provided |
|
57 |
when the ``Validator`` is instantiated, or added afterwards. |
|
58 |
||
59 |
Validate was primarily written to support ConfigObj_, but was designed to be |
|
60 |
applicable to many other situations. |
|
61 |
||
62 |
For support and bug reports please use the ConfigObj `Mailing List`_. |
|
63 |
||
64 |
.. _ConfigObj: http://www.voidspace.org.uk/python/configobj.html |
|
65 |
||
66 |
Downloading |
|
67 |
=========== |
|
68 |
||
69 |
The current version is **0.2.0**, dated 25th August 2005. |
|
70 |
||
71 |
You can get obtain validate in the following ways : |
|
72 |
||
73 |
Files |
|
74 |
----- |
|
75 |
||
76 |
* validate.py_ from Voidspace |
|
77 |
||
78 |
* configobj.zip from Voidspace - See the homepage of ConfigObj_ for the latest |
|
79 |
version and downlaod links. |
|
80 |
||
81 |
This contains validate.py and `this document`_. (As well as ConfigObj_ and |
|
82 |
the ConfigObj documentation). |
|
83 |
||
84 |
* The latest development version can be obtained from the `Subversion Repository`_. |
|
85 |
||
86 |
Documentation |
|
87 |
------------- |
|
88 |
||
89 |
*configobj.zip* contains `this document`_ and full `API Docs`_, generated |
|
90 |
automatically by EpyDoc_. |
|
91 |
||
92 |
* You can view `this document`_ online as the `Validate Homepage`_. |
|
93 |
||
94 |
* You can also browse the `API Docs`_ online. |
|
95 |
||
96 |
Pythonutils |
|
97 |
----------- |
|
98 |
||
99 |
Validate_ is also part of the Pythonutils_ set of modules. This contains |
|
100 |
various other useful helper modules, and is required by many of the `Voidspace |
|
101 |
Python Projects`_. |
|
102 |
||
103 |
.. _configobj.py: http://www.voidspace.org.uk/cgi-bin/voidspace/downman.py?file=configobj.py |
|
104 |
.. _configobj.zip: http://www.voidspace.org.uk/cgi-bin/voidspace/downman.py?file=configobj-4.0.0b4.zip |
|
105 |
.. _validate.py: http://www.voidspace.org.uk/cgi-bin/voidspace/downman.py?file=validate.py |
|
106 |
.. _API Docs: http://www.voidspace.org.uk/python/configobj-api/ |
|
107 |
.. _Subversion Repository: http://svn.rest2web.python-hosting.com/branches/configobj4/ |
|
108 |
.. _Sourceforge: http://sourceforge.net/projects/configobj |
|
109 |
.. _EpyDoc: http://epydoc.sourceforge.net |
|
110 |
.. _pythonutils: http://www.voidspace.org.uk/python/pythonutils.html |
|
111 |
.. _Voidspace Python Projects: http://www.voidspace.org.uk/python |
|
112 |
.. _validate: http://www.voidspace.org.uk/python/validate.html |
|
113 |
||
114 |
||
115 |
The standard functions |
|
116 |
====================== |
|
117 |
||
118 |
The standard functions come built-in to every ``Validator`` instance. They work |
|
119 |
with the following basic data types : |
|
120 |
||
121 |
* integer |
|
122 |
* float |
|
123 |
* boolean |
|
124 |
* string |
|
125 |
* ip_addr |
|
126 |
||
127 |
plus lists of these datatypes. |
|
128 |
||
129 |
Adding additional checks is done through coding simple functions. |
|
130 |
||
131 |
The full set of standard checks are : |
|
132 |
||
133 |
:'integer': matches integer values (including negative). Takes optional 'min' |
|
134 |
and 'max' arguments : :: |
|
135 |
||
136 |
integer() |
|
137 |
integer(3, 9) # any value from 3 to 9 |
|
138 |
integer(min=0) # any positive value |
|
139 |
integer(max=9) |
|
140 |
||
141 |
:'float': matches float values |
|
142 |
Has the same parameters as the integer check. |
|
143 |
||
144 |
:'bool': matches boolean values: ``True`` or ``False``. |
|
145 |
Acceptable string values for True are : :: |
|
146 |
||
147 |
true, on, yes, 1 |
|
148 |
||
149 |
Acceptable string values for False are : :: |
|
150 |
||
151 |
false, off, no, 0 |
|
152 |
||
153 |
Any other value raises an error. |
|
154 |
||
155 |
:'string': matches any string. Takes optional keyword args 'min' and 'max' to |
|
156 |
specify min and max length of string. |
|
157 |
||
158 |
:'ip_addr': matches an Internet Protocol address, v.4, represented by a |
|
159 |
dotted-quad string, i.e. '1.2.3.4'. |
|
160 |
||
161 |
:'list': matches any list. Takes optional keyword args 'min', and 'max' to |
|
162 |
specify min and max sizes of the list. |
|
163 |
||
164 |
:'int_list': Matches a list of integers. Takes the same arguments as list. |
|
165 |
||
166 |
:'float_list': Matches a list of floats. Takes the same arguments as list. |
|
167 |
||
168 |
:'bool_list': Matches a list of boolean values. Takes the same arguments as |
|
169 |
list. |
|
170 |
||
171 |
:'string_list': Matches a list of strings. Takes the same arguments as list. |
|
172 |
||
173 |
:'ip_addr_list': Matches a list of IP addresses. Takes the same arguments as |
|
174 |
list. |
|
175 |
||
176 |
:'mixed_list': Matches a list with different types in specific positions. |
|
177 |
List size must match the number of arguments. |
|
178 |
||
179 |
Each position can be one of : :: |
|
180 |
||
181 |
int, str, bool, float, ip_addr |
|
182 |
||
183 |
So to specify a list with two strings followed by two integers, |
|
184 |
you write the check as : :: |
|
185 |
||
186 |
mixed_list(str, str, int, int) |
|
187 |
||
188 |
:'pass': matches everything: it never fails and the value is unchanged. It is |
|
189 |
also the default if no check is specified. |
|
190 |
||
191 |
:'option': matches any from a list of options. |
|
192 |
You specify this test with : :: |
|
193 |
||
194 |
option('option 1', 'option 2', 'option 3')
|
|
195 |
||
196 |
The following code will work without you having to specifically add the |
|
197 |
functions yourself. |
|
198 |
||
199 |
.. raw:: html |
|
200 |
||
201 |
{+coloring}
|
|
202 |
||
203 |
from validate import Validator |
|
204 |
# |
|
205 |
vtor = Validator() |
|
206 |
newval1 = vtor.check(value1, 'integer') |
|
207 |
newval2 = vtor.check(value2, 'bool') |
|
208 |
# etc ... |
|
209 |
||
210 |
{-coloring}
|
|
211 |
||
212 |
.. note:: |
|
213 |
||
214 |
Of course, if these checks fail they raise exceptions. So you should wrap |
|
215 |
them in ``try...except`` blocks. Better still, use ConfigObj for a higher |
|
216 |
level interface. |
|
217 |
||
218 |
Using Validator |
|
219 |
=============== |
|
220 |
||
221 |
Using ``Validator`` is very easy. It has one public attribute and one public |
|
222 |
method. |
|
223 |
||
224 |
Shown below are the different steps in using ``Validator``. |
|
225 |
||
226 |
The only additional thing you need to know, is about `Writing check |
|
227 |
functions`_. |
|
228 |
||
229 |
Instantiate |
|
230 |
----------- |
|
231 |
||
232 |
.. raw:: html |
|
233 |
||
234 |
{+coloring}
|
|
235 |
||
236 |
from validate import Validator |
|
237 |
vtor = Validator() |
|
238 |
||
239 |
{-coloring}
|
|
240 |
||
241 |
or even : |
|
242 |
||
243 |
.. raw:: html |
|
244 |
||
245 |
{+coloring}
|
|
246 |
||
247 |
from validate import Validator |
|
248 |
# |
|
249 |
fdict = {
|
|
250 |
'check_name1': function1, |
|
251 |
'check_name2': function2, |
|
252 |
'check_name3': function3, |
|
253 |
} |
|
254 |
# |
|
255 |
vtor = Validator(fdict) |
|
256 |
||
257 |
{-coloring}
|
|
258 |
||
259 |
The second method adds a set of your functions as soon as your validator is |
|
260 |
created. They are stored in the ``vtor.functions`` dictionary. The 'key' you |
|
261 |
give them in this dictionary is the name you use in your checks (not the |
|
262 |
original function name). |
|
263 |
||
264 |
Dictionary keys/functions you pass in can override the built-in ones if you |
|
265 |
want. |
|
266 |
||
267 |
Adding functions |
|
268 |
---------------- |
|
269 |
||
270 |
The code shown above, for adding functions on instantiation, has exactly the |
|
271 |
same effect as the following code : |
|
272 |
||
273 |
.. raw:: html |
|
274 |
||
275 |
{+coloring}
|
|
276 |
||
277 |
from validate import Validator |
|
278 |
# |
|
279 |
vtor = Validator() |
|
280 |
vtor.functions['check_name1'] = function1 |
|
281 |
vtor.functions['check_name2'] = function2 |
|
282 |
vtor.functions['check_name3'] = function3 |
|
283 |
||
284 |
{-coloring}
|
|
285 |
||
286 |
``vtor.functions``is just a dictionary that maps names to functions, so we |
|
287 |
could also have called ``vtor.functions.update(fdict)``. |
|
288 |
||
289 |
Writing the check |
|
290 |
----------------- |
|
291 |
||
292 |
As we've heard, the checks map to the names in the ``functions`` dictionary. |
|
293 |
You've got a full list of `The standard functions`_ and the arguments they |
|
294 |
take. |
|
295 |
||
296 |
If you're using ``Validator`` from ConfigObj, then your checks will look like |
|
297 |
: :: |
|
298 |
||
299 |
keyword = int_list(max=6) |
|
300 |
||
301 |
but the check part will be identical . |
|
302 |
||
303 |
The check method |
|
304 |
---------------- |
|
305 |
||
306 |
If you're not using ``Validator`` from ConfigObj, then you'll need to call the |
|
307 |
``check`` method yourself. |
|
308 |
||
309 |
If the check fails then it will raise an exception, so you'll want to trap |
|
310 |
that. Here's the basic example : |
|
311 |
||
312 |
.. raw:: html |
|
313 |
||
314 |
{+coloring}
|
|
315 |
||
316 |
from validate import Validator, ValidateError |
|
317 |
# |
|
318 |
vtor = Validator() |
|
319 |
check = "integer(0, 9)" |
|
320 |
value = 3 |
|
321 |
try: |
|
322 |
newvalue = vtor.check(check, value) |
|
323 |
except ValidateError: |
|
324 |
print 'Check Failed.' |
|
325 |
else: |
|
326 |
print 'Check passed.' |
|
327 |
||
328 |
{-coloring}
|
|
329 |
||
330 |
.. caution:: |
|
331 |
||
332 |
Although the value can be a string, if it represents a list it should |
|
333 |
already have been turned into a list of strings. |
|
334 |
||
335 |
Default Values |
|
336 |
~~~~~~~~~~~~~~ |
|
337 |
||
338 |
Some values may not be available, and you may want to be able to specify a |
|
339 |
default as part of the check. |
|
340 |
||
341 |
You do this by passing the keyword ``missing=True`` to the ``check`` method, as |
|
342 |
well as a ``default=value`` in the check. (Constructing these checks is done |
|
343 |
automatically by ConfigObj: you only need to know about the ``default=value`` |
|
344 |
part) : |
|
345 |
||
346 |
.. raw:: html |
|
347 |
||
348 |
{+coloring}
|
|
349 |
||
350 |
check1 = 'integer(default=50)' |
|
351 |
check2 = 'option("val 1", "val 2", "val 3", default="val 1")'
|
|
352 |
||
353 |
assert vtor.check('', check1, missing=True) == 50
|
|
354 |
assert vtor.check('', check2, missing=True) == "val 1"
|
|
355 |
||
356 |
{-coloring}
|
|
357 |
||
358 |
If you pass in ``missing=True`` to the check method, then the actual value is |
|
359 |
ignored. If no default is specified in the check, a ``ValidateMissingValue`` |
|
360 |
exception is raised. If a default is specified then that is passed to the |
|
361 |
check instead. |
|
362 |
||
363 |
If the check has ``default=None`` (case sensitive) then ``vtor.check`` will |
|
364 |
*always* return ``None`` (the object). This makes it easy to tell your program |
|
365 |
that this check contains no useful value when missing, i.e. the value is |
|
366 |
optional, and may be omitted without harm. |
|
367 |
||
368 |
Validator Exceptions |
|
369 |
==================== |
|
370 |
||
371 |
.. note:: |
|
372 |
||
373 |
If you only use Validator through ConfigObj, it traps these Exceptions for |
|
374 |
you. You will still need to know about them for writing your own check |
|
375 |
functions. |
|
376 |
||
377 |
``vtor.check`` indicates that the check has failed by raising an exception. |
|
378 |
The appropriate error should be raised in the check function. |
|
379 |
||
380 |
The base error class is ``ValidateError``. All errors (except for ``VdtParamError``) |
|
381 |
raised are sub-classes of this. |
|
382 |
||
383 |
If an unrecognised check is specified then ``VdtUnknownCheckError`` is |
|
384 |
raised. |
|
385 |
||
386 |
There are also ``VdtTypeError`` and ``VdtValueError``. |
|
387 |
||
388 |
If incorrect parameters are passed to a check function then it will (or should) |
|
389 |
raise ``VdtParamError``. As this indicates *programmer* error, rather than an error |
|
390 |
in the value, it is a subclass of ``SyntaxError`` instead of ``ValidateError``. |
|
391 |
||
392 |
.. note:: |
|
393 |
||
394 |
This means it *won't* be caught by ConfigObj - but propagated instead. |
|
395 |
||
396 |
If the value supplied is the wrong type, then the check should raise |
|
397 |
``VdtTypeError``. e.g. the check requires the value to be an integer (or |
|
398 |
representation of an integer) and something else was supplied. |
|
399 |
||
400 |
If the value supplied is the right type, but an unacceptable value, then the |
|
401 |
check should raise ``VdtValueError``. e.g. the check requires the value to |
|
402 |
be an integer (or representation of an integer) less than ten and a higher |
|
403 |
value was supplied. |
|
404 |
||
405 |
Both ``VdtTypeError`` and ``VdtValueError`` are initialised with the |
|
406 |
incorrect value. In other words you raise them like this : |
|
407 |
||
408 |
.. raw:: html |
|
409 |
||
410 |
{+coloring}
|
|
411 |
||
412 |
raise VdtTypeError(value) |
|
413 |
# |
|
414 |
raise VdtValueError(value) |
|
415 |
||
416 |
{-coloring}
|
|
417 |
||
418 |
``VdtValueError`` has the following subclasses, which should be raised if |
|
419 |
they are more appropriate. |
|
420 |
||
421 |
* ``VdtValueTooSmallError`` |
|
422 |
* ``VdtValueTooBigError`` |
|
423 |
* ``VdtValueTooShortError`` |
|
424 |
* ``VdtValueTooLongError`` |
|
425 |
||
426 |
Writing check functions |
|
427 |
======================= |
|
428 |
||
429 |
Writing check functions is easy. |
|
430 |
||
431 |
The check function will receive the value as its first argument, followed by |
|
432 |
any other parameters and keyword arguments. |
|
433 |
||
434 |
If the check fails, it should raise a ``VdtTypeError`` or a |
|
435 |
``VdtValueError`` (or an appropriate subclass). |
|
436 |
||
437 |
All parameters and keyword arguments are *always* passed as strings. (Parsed |
|
438 |
from the check string). |
|
439 |
||
440 |
The value might be a string (or list of strings) and need |
|
441 |
converting to the right type - alternatively it might already be a list of |
|
442 |
integers. Our function needs to be able to handle either. |
|
443 |
||
444 |
If the check passes then it should return the value (possibly converted to the |
|
445 |
right type). |
|
446 |
||
447 |
And that's it ! |
|
448 |
||
449 |
Example |
|
450 |
------- |
|
451 |
||
452 |
Here is an example function that requires a list of integers. Each integer |
|
453 |
must be between 0 and 99. |
|
454 |
||
455 |
It takes a single argument specifying the length of the list. (Which allows us |
|
456 |
to use the same check in more than one place). If the length can't be converted |
|
457 |
to an integer then we need to raise ``VdtParamError``. |
|
458 |
||
459 |
Next we check that the value is a list. Anything else should raise a |
|
460 |
``VdtTypeError``. The list should also have 'length' entries. If the list |
|
461 |
has more or less entries then we will need to raise a |
|
462 |
``VdtValueTooShortError`` or a ``VdtValueTooLongError``. |
|
463 |
||
464 |
Then we need to check every entry in the list. Each entry should be an integer |
|
465 |
between 0 and 99, or a string representation of an integer between 0 and 99. |
|
466 |
Any other type is a ``VdtTypeError``, any other value is a |
|
467 |
``VdtValueError`` (either too big, or too small). |
|
468 |
||
469 |
.. raw:: html |
|
470 |
||
471 |
{+coloring}
|
|
472 |
||
473 |
def special_list(value, length): |
|
474 |
""" |
|
475 |
Check that the supplied value is a list of integers, |
|
476 |
with 'length' entries, and each entry between 0 and 99. |
|
477 |
""" |
|
478 |
# length is supplied as a string |
|
479 |
# we need to convert it to an integer |
|
480 |
try: |
|
481 |
length = int(length) |
|
482 |
except ValueError: |
|
483 |
raise VdtParamError('length', length)
|
|
484 |
# |
|
485 |
# Check the supplied value is a list |
|
486 |
if not isinstance(value, list): |
|
487 |
raise VdtTypeError(value) |
|
488 |
# |
|
489 |
# check the length of the list is correct |
|
490 |
if len(value) > length: |
|
491 |
raise VdtValueTooLongError(value) |
|
492 |
elif len(value) < length: |
|
493 |
raise VdtValueTooShortError(value) |
|
494 |
# |
|
495 |
# Next, check every member in the list |
|
496 |
# converting strings as necessary |
|
497 |
out = [] |
|
498 |
for entry in value: |
|
499 |
if not isinstance(entry, (str, unicode, int)): |
|
500 |
# a value in the list |
|
501 |
# is neither an integer nor a string |
|
502 |
raise VdtTypeError(value) |
|
503 |
elif isinstance(entry, (str, unicode)): |
|
504 |
if not entry.isdigit(): |
|
505 |
raise VdtTypeError(value) |
|
506 |
else: |
|
507 |
entry = int(entry) |
|
508 |
if entry < 0: |
|
509 |
raise VdtValueTooSmallError(value) |
|
510 |
elif entry > 99: |
|
511 |
raise VdtValueTooBigError(value) |
|
512 |
out.append(entry) |
|
513 |
# |
|
514 |
# if we got this far, all is well |
|
515 |
# return the new list |
|
516 |
return out |
|
517 |
||
518 |
{-coloring}
|
|
519 |
||
520 |
If you are only using validate from ConfigObj then the error type (*TooBig*, |
|
521 |
*TooSmall*, etc) is lost - so you may only want to raise ``VdtValueError``. |
|
522 |
||
523 |
.. caution:: |
|
524 |
||
525 |
If your function raises an exception that isn't a subclass of |
|
526 |
``ValidateError``, then ConfigObj won't trap it. This means validation will |
|
527 |
fail. |
|
528 |
||
529 |
This is why our function starts by checking the type of the value. If we |
|
530 |
are passed the wrong type (e.g. an integer rather than a list) we get a |
|
531 |
``VdtTypeError`` rather than bombing out when we try to iterate over |
|
532 |
the value. |
|
533 |
||
534 |
If you are using validate in another circumstance you may want to create your |
|
535 |
own subclasses of ``ValidateError``, that convey more specific information. |
|
536 |
||
537 |
TODO |
|
538 |
==== |
|
539 |
||
540 |
* A regex check function ? |
|
541 |
* A timestamp check function ? (Using the ``parse`` function from ``DateUtil``). |
|
542 |
* Allow triple quotes ? (getting a bit heavy for a single regex) |
|
543 |
||
544 |
ISSUES |
|
545 |
====== |
|
546 |
||
547 |
.. note:: |
|
548 |
||
549 |
Please file any bug reports to `Michael Foord`_ or the ConfigObj |
|
550 |
`Mailing List`_. |
|
551 |
||
552 |
Lists passed as function arguments need additional quotes. Ugly, could do |
|
553 |
with fixing this. |
|
554 |
||
555 |
If we could pull tuples out of arguments, it would be easier |
|
556 |
to specify arguments for 'mixed_lists'. |
|
557 |
||
558 |
CHANGELOG |
|
559 |
========= |
|
560 |
||
561 |
||
562 |
2005/08/18 Version 0.2.0 |
|
563 |
----------------------------- |
|
564 |
||
565 |
Updated by `Michael Foord`_ and `Nicola Larosa`_ |
|
566 |
||
567 |
Does type conversion as well. |
|
568 |
||
569 |
2005/02/01 Version 0.1.0 |
|
570 |
----------------------------- |
|
571 |
||
572 |
Initial version developed by `Michael Foord`_ |
|
573 |
and `Mark Andrews`_ |
|
574 |
||
575 |
.. note:: |
|
576 |
||
577 |
Rendering this document with docutils also needs the |
|
578 |
textmacros module and the PySrc CSS stuff. See |
|
579 |
http://www.voidspace.org.uk/python/firedrop2/textmacros.shtml |
|
580 |
||
581 |
.. raw:: html |
|
582 |
||
583 |
<div align="center"> |
|
584 |
<a href="http://www.python.org"> |
|
585 |
<img src="images/powered_by_python.jpg" width="602" height="186" border="0" /> |
|
586 |
</a> |
|
587 |
<a href="http://www.opensource.org"> |
|
588 |
<img src="images/osi-certified-120x100.gif" width="120" height="100" border="0" /> |
|
589 |
<br /><strong>Certified Open Source</strong> |
|
590 |
</a> |
|
591 |
<br /><br /> |
|
592 |
<script type="text/javascript" language="JavaScript">var site="s16atlantibots"</script> |
|
593 |
<script type="text/javascript" language="JavaScript1.2" src="http://s16.sitemeter.com/js/counter.js?site=s16atlantibots"></script> |
|
594 |
<noscript> |
|
595 |
<a href="http://s16.sitemeter.com/stats.asp?site=s16atlantibots"> |
|
596 |
<img src="http://s16.sitemeter.com/meter.asp?site=s16atlantibots" alt="Site Meter" border=0 /> |
|
597 |
</a> |
|
598 |
</noscript> |
|
599 |
</div> |