Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Passing string values from ADO to Python code

    Happy New Year, All!

    I am having a rather trivial problem here, for which I couldn't figure out a solution yet.

    I need to pass string values from ADO code to Python code, and the strings may have any characters, including the quotes (potentially unbalanced) and dollar signs.
    Since the characters in the string values are user input, I can't precode them with e.g. a char() function as a constant, etc.

    Consider the following example (save this as adoprint.ado):

    Code:
    version 16.0
    
    program define adoprint
      syntax , param(string) 
      display _asis `"`macval(param)'"'
      python : pyprint("`macval(param)'")  
      // ^^^ doesn't seem to support `" "' quotes here, since this is a python statement
    end
    
    python:
    def pyprint(p):
      print(p)  # here must be able to print any string parameter p passed to this procedure asis!
    end
    
    // END OF FILE
    We'd then run the following test file:
    Code:
    clear all
    capture noisily adoprint, param("x=45")       // works fine
    capture noisily adoprint, param(`"x="$45""')  // results in a syntax error
    capture noisily adoprint, param(`"x="\$45""') // results in a syntax error
    // END OF FILE

    I am looking for a solution that would allow me to pass any combination of characters from ado code to python and access it in python code (in the example it is trivial printing, but in reality the string is meaningfully processed).

    Any characters must be supported in any combination, but of specific concern are, of course, the quotes, double quotes, dollar sign, figure brackets, and other characters which may be triggering Stata syntax reactions. In the adocode (as demonstrated in the above example) I can utilize the _asis modifier of the display command and utilize the compound quotes to achieve relative safety, but this doesn't seem to work for calling the python procedure.

    The best I can do at this time, is to avoid passing the value as a parameter, and peek directly into the local macro as defined in Stata's scope with Macro.getLocal("param") from the Python code. But I'd rather avoid this approach and have declared parameters for the sake of transparency of the interface.

    Thank you, Sergiy

  • #2
    Code:
    program define adoprint
      syntax , param(string asis) 
      python :  print(""" `param' """)
    end

    Comment


    • #3
      The syntax error being thrown lies in python, I believe, in that a string literal surrounded by double quotes cannot contain a double quote within it, and a string literal surrounded by single quotes cannot contain a single quote within it. Or is there some python syntax for getting around that restriction, like compound single quotes in Stata or the various quote() functions in SAS? As you correctly point out, the text to the right of "python :" is a - after Stata macro substitution - a python command.

      I think you might want to compromise by passing to pyprint a literal string containing the name of the local macro to retrieve the value of. Something like
      Code:
      program define adoprint
        syntax , param(string)
        python : pyprint('param')  // using single quotes to emphasize that this is a python string literal in a python command, not a Stata string literal
      end
      
      python:
      def pyprint(p):
        print(Macro.getLocal(p))  # here must be able to print any string parameter p passed to this procedure asis!
      end
      Unfortunately, I'm not facile enough with python to test this.

      Added in edit: crossed with #2, which seems to answer my question posed in the first paragraph. My source of python knowledge did not discuss using triple quotation marks to define string literals.
      Last edited by William Lisowski; 02 Jan 2021, 15:18.

      Comment


      • #4
        Dear Bjarte Aagnes , thank you very much for hinting at """ , but it still doesn't solve it completely, as I have no insurance that the combination """ will not occur in the param value.

        Consider the following test case:
        Code:
        capture noisily adoprint, param(x= `=char(96)'`=char(34)'`=char(34)'`=char(34)'`=char(39)')
        Here I am constructing it artificially, it real world it might end up being a line read from a text file.
        This will be substituted into the python call line, and cause a syntax error.
        Hence for the moment I see no alternative to Macro.getLocal even though the value is displayed correctly in the augmented adoprint:

        Code:
        program define adoprint
          syntax , param(string asis) 
          display _asis `"`macval(param)'"'
          python :  print(""" `macval(param)' """)
        end
        Code:
        . capture noisily adoprint, param(x= `=char(96)'`=char(34)'`=char(34)'`=char(34)'`=char(39)')
          ---------------------------------------------------------------------------------------------------- begin adoprint ---
          - syntax , param(string asis)
          - display _asis `"`macval(param)'"'
          = display _asis `"x= `"""'"'
        x= `"""'
          - python : print(""" `macval(param)' """)
          = python : print(""" x= `"""' """)
          File "<stdin>", line 1
            print(""" x= `"""' """)
                                  ^
        SyntaxError: EOL while scanning string literal
          ------------------------------------------------------------------------------------------------------ end adoprint ---
        
        . // END OF FILE
        Thank you, Sergiy

        Comment


        • #5
          it might end up being a line read from a text file
          Seems to be the only option for passing any characters in any combination. Below; escaping " enables """ in Python string:
          Code:
          tempfile args
          qui di filewrite("`args'", ///
              3 * char(96) ///
              + 3 * char(34) ///
              + char(39) ///
              + char(36) + "name" ///
              + char(96) ///
              + char(39) ///
              + "[({" , 1 ) 
          
          local s = subinstr(fileread("`args'"), char(34), "\" + char(34), .)
          python :  print(""" param(x=`macval(s)') """)
          Code:
          param(x=```"""'$name`'[({)
          Last edited by Bjarte Aagnes; 03 Jan 2021, 09:15.

          Comment


          • #6
            Dear Bjarte Aagnes and William Lisowski , thank you very much for your helpful hints. I've opted for two solutions (which I use in different situations):
            1. peeking the local in Stata with the Macro.getLocal()
            2. passing the filename as a parameter to the python routine, where I can have more flexibility in dealing with its content.
            Sincerely, Sergiy

            Comment

            Working...
            X