# How expensive is each WebDev route

**URL:** <https://forum.inductiveautomation.com/t/how-expensive-is-each-webdev-route/104691>\
**Category:** Ignition\
**Tags:** ignition81\
**Created:** [April 4, 2025, 2:22am UTC](https://forum.inductiveautomation.com/t/how-expensive-is-each-webdev-route/104691 "2025-04-04T02:22:30Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![Leor\_Fishman](https://avatars.discourse-cdn.com/v4/letter/l/e19adc/32.png) [@Leor\_Fishman](https://forum.inductiveautomation.com/u/Leor_Fishman)\
**Post date:** [April 4, 2025, 2:22am UTC](https://forum.inductiveautomation.com/t/how-expensive-is-each-webdev-route/104691/1 "2025-04-04T02:22:30Z")

</div>

first off, i promise this is distinct from my other questions (except for the py3 one)

how many web dev routes can a gateway easily run?

in particular, if someone were to programmatically generate...as many web dev routes as there are ignition system functions

would this break anything?

---

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [April 4, 2025, 2:23am UTC](https://forum.inductiveautomation.com/t/how-expensive-is-each-webdev-route/104691/2 "2025-04-04T02:23:13Z")

</div>

Find out and report back 🙂

---

<div class="post-metadata">

**Author:** ![Leor\_Fishman](https://avatars.discourse-cdn.com/v4/letter/l/e19adc/32.png) [@Leor\_Fishman](https://forum.inductiveautomation.com/u/Leor_Fishman)\
**Post date:** [April 4, 2025, 2:23am UTC](https://forum.inductiveautomation.com/t/how-expensive-is-each-webdev-route/104691/3 "2025-04-04T02:23:56Z")

</div>

i might have to try this

if it works, i might well have a py3 scripting integration approach 🙂

---

<div class="post-metadata">

**Author:** ![Leor\_Fishman](https://avatars.discourse-cdn.com/v4/letter/l/e19adc/32.png) [@Leor\_Fishman](https://forum.inductiveautomation.com/u/Leor_Fishman)\
**Post date:** [April 4, 2025, 2:25am UTC](https://forum.inductiveautomation.com/t/how-expensive-is-each-webdev-route/104691/4 "2025-04-04T02:25:17Z")

</div>

Is there an easy list of which system functions are idempotent?

---

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [April 4, 2025, 2:47am UTC](https://forum.inductiveautomation.com/t/how-expensive-is-each-webdev-route/104691/5 "2025-04-04T02:47:42Z")

</div>

There is not.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [April 4, 2025, 3:41pm UTC](https://forum.inductiveautomation.com/t/how-expensive-is-each-webdev-route/104691/6 "2025-04-04T15:41:35Z")

</div>

You'll get a (lazy loaded), not completely insignificant though not terrible, hit to memory usage for each route loaded. You may not need lots of routes, though... if you're just trying to do something like `webdev/scriptingProject/system/abc/xyz`, you can use the [`remainingPath` parameter](https://docs.inductiveautomation.com/docs/8.1/ignition-modules/web-dev#request-parameter) to mount a "root" handler that steps through the lower indirection. No need to seed the system with a bunch of semi-hardcoded things.

---

<div class="post-metadata">

**Author:** ![Leor\_Fishman](https://avatars.discourse-cdn.com/v4/letter/l/e19adc/32.png) [@Leor\_Fishman](https://forum.inductiveautomation.com/u/Leor_Fishman)\
**Post date:** [April 4, 2025, 10:57pm UTC](https://forum.inductiveautomation.com/t/how-expensive-is-each-webdev-route/104691/7 "2025-04-04T22:57:55Z")

</div>

silly paul, the more i get nerdsniped on this the less i work on [REDACTED PROJECT A] and 'py3 scripting support for ignition based on a wrapper lib and eval'

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [April 4, 2025, 10:58pm UTC](https://forum.inductiveautomation.com/t/how-expensive-is-each-webdev-route/104691/8 "2025-04-04T22:58:50Z")

</div>

> [@Leor\_Fishman](#):
>
> and eval'

Do be surprised by the lack of uptake with that in it.

---

<div class="post-metadata">

**Author:** ![Leor\_Fishman](https://avatars.discourse-cdn.com/v4/letter/l/e19adc/32.png) [@Leor\_Fishman](https://forum.inductiveautomation.com/u/Leor_Fishman)\
**Post date:** [April 4, 2025, 11:01pm UTC](https://forum.inductiveautomation.com/t/how-expensive-is-each-webdev-route/104691/9 "2025-04-04T23:01:52Z")

</div>

Yeah, in practice I probably wouldn't use eval, but instead would be using ast.compile and walking the globals dict -- if you've got a better way to avoid 'writing a web dev route for every individual system function' i'm all ears

(This is probably a matter for a separate thread, but the short version of the approach is 'wrapper around system functions by having the function calls get routed through a webdev handler that serializes out the response exceptions and outputs)

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [April 4, 2025, 11:06pm UTC](https://forum.inductiveautomation.com/t/how-expensive-is-each-webdev-route/104691/10 "2025-04-04T23:06:17Z")

</div>

> [@Leor\_Fishman](#):
>
> using ast.compile

Uh, not any better. Sheesh. Paul gave you the answer over in the webdev topic: use remaining path, split on '/', use `globals().get()` on the first name, and then `getattr()` for subsequent names. Decode the payload into a list of argments and/or dictionary of keyword arguments. Call function. Encode response.

No need for eval or compile or any other hackable remote code injection.

🤷‍♂️

---

<div class="post-metadata">

**Author:** ![Leor\_Fishman](https://avatars.discourse-cdn.com/v4/letter/l/e19adc/32.png) [@Leor\_Fishman](https://forum.inductiveautomation.com/u/Leor_Fishman)\
**Post date:** [April 4, 2025, 11:07pm UTC](https://forum.inductiveautomation.com/t/how-expensive-is-each-webdev-route/104691/11 "2025-04-04T23:07:56Z")

</div>

Does globals.get resolve import paths too? that's what I was worried about on the ast.compile side. (I would have been dropping all non-function name pieces of the AST after compiling).

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [April 4, 2025, 11:10pm UTC](https://forum.inductiveautomation.com/t/how-expensive-is-each-webdev-route/104691/12 "2025-04-04T23:10:43Z")

</div>

No, it resolves what is reachable in the global module of the calling function. `system` will be there, along with all of the project library scripts of that project. Use `dir()` in a WebDev `doGet()` for more information. (You may want to constrain that first name lookup.)

Really, don't eval() or compile. Those are terrible for performance, dangerous, and just not necessary.

---

<div class="post-metadata">

**Author:** ![Leor\_Fishman](https://avatars.discourse-cdn.com/v4/letter/l/e19adc/32.png) [@Leor\_Fishman](https://forum.inductiveautomation.com/u/Leor_Fishman)\
**Post date:** [April 4, 2025, 11:12pm UTC](https://forum.inductiveautomation.com/t/how-expensive-is-each-webdev-route/104691/13 "2025-04-04T23:12:38Z")

</div>

...sorry, just realized I mixed up compile and ast.parse, that would explain some of the concern

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [April 4, 2025, 11:37pm UTC](https://forum.inductiveautomation.com/t/how-expensive-is-each-webdev-route/104691/14 "2025-04-04T23:37:59Z")

</div>

Rephrasing some of Phil's point:

Everywhere you run a script inside Ignition, we're _automatically_ injecting a special object in under the keyword `system`. \[1\] That object, naturally, contains all the sub-namespaced functions (`alarm`, `dataset`, `perspective` \[2\], etc) - and those sub namespaces have all the inner functions, as standard Python callable objects.

That is, the _one_ hack we're doing is automatically injecting `system` into the local scope. We are **not** attempting to do something like "find every usage of `system.x.y` in the string and hot-replace it with a dynamic hook to Java code" or something else insane. If you have a reference to `system`, it's a Python object like any other - same as the objects below it in the namespace. You can even do highly questionable things like:

```python
db = system.db
exportCSV = system.dataset.exportCSV

```

And so on. So any of the standard Python metaprogramming stuff like `getattr` will "just work"; the below two lines are functionally equivalent:

```python
db = system.db
db = getattr(system, "db")

```

* * *

1. also all the project library scripts, but leaving that questionable past decision aside 

2. in appropriate scope

---

<div class="post-metadata">

**Author:** ![Leor\_Fishman](https://avatars.discourse-cdn.com/v4/letter/l/e19adc/32.png) [@Leor\_Fishman](https://forum.inductiveautomation.com/u/Leor_Fishman)\
**Post date:** [April 4, 2025, 11:44pm UTC](https://forum.inductiveautomation.com/t/how-expensive-is-each-webdev-route/104691/15 "2025-04-04T23:44:40Z")

</div>

Ahhhhh, I hadn't realized that -- that does make this significantly easier
